0
点赞
收藏
分享

微信扫一扫

Java、PHP、Node 操作 MySQL 数据库常用方法

40dba2f2a596 2024-08-06 阅读 14
🍁你好,我是 RO-BERRY
📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识
🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油

请添加图片描述


目录


1. 创建线程

功能:创建一个新的线程

在这里插入图片描述

参数

  • thread:返回线程ID
  • attr:设置线程的属性,attr为NULL表示使用默认属性
  • tart_routine:是个函数地址,线程启动后要执行的函数
  • rg:传给线程启动函数的参数
  • 返回值:成功返回0;失败返回错误码

【错误检查】

Makefile

testThread:testThread.cc
	g++ -o $@ $^ -lpthread
.PHONY:clean
clean:
	rm -f testThread

testThread.cc

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>

int gcnt = 100;

// 新线程
void *ThreadRoutine(void *arg)
{
    const char *threadname = (const char *)arg;
    while (true)
    {
        std::cout << "I am a new thread: " << threadname << ", pid: " << getpid()<< std::endl;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread 1");

    while (true)
    {
        std::cout << "I am main thread"<< std::endl;
        sleep(1);
    }
    return 0;
}

运行结果:

在这里插入图片描述
可以看到确实是有了两个线程都在运行,这里的线程就是一个是我们的main主线程,另一个则是我们新创建的线程。

在这里插入图片描述

接下来我们来认识一下POSIX线程库


2. POSIX线程库

  • 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
  • 要使用这些函数库,要通过引入头文<pthread.h>
  • 链接这些线程函数库时要使用编译器命令的“-lpthread”选项

POSIX线程库,也称为pthread,是一种跨平台的标准API,它为应用程序提供了一套统一的方式来创建和管理线程。

  • 由于Unix-like操作系统(如Linux、macOS)的广泛使用,POSIX标准允许开发者编写一次代码就能在多种平台上运行,避免了为每种操作系统单独编写和维护线程代码的工作量。

  • POSIX线程使得程序可以将并发处理的部分封装成独立的模块,便于模块间的通信和协同工作,提高代码的灵活性和效率。

  • 标准化的线程API保证了不同系统之间的线程行为基本一致,有利于开发者理解和预测其行为,同时也有助于优化线程管理,提升系统的整体性能。

  • POSIX线程库提供了丰富的错误处理机制和同步原语(如互斥锁、条件变量),有助于开发者设计出健壮且安全的多线程应用。

  • 虽然直接操作底层硬件线程可能会更复杂,但是通过POSIX API,开发者能以相对简单的方式实现复杂的并发控制,降低学习曲线。
    在这里插入图片描述

开发者将pthread线程库开发成这样就是为了线程安全并且实现跨平台的性能,让OS能通过线程库提供API给用户才能实现线程的操作

另外需要注意的是,在linux中,如何对这些线程进行管理呢?


3. 线程ID及进程地址空间布局

  • pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID不是一回事。
  • 前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程。
  • pthread_ create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,
  • 属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。
  • 线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID

在这里插入图片描述

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>

// 新线程
void *ThreadRoutine(void *arg)
{
    const char *threadname = (const char *)arg;
    while (true)
    {
        std::cout << "I am a new thread: " << threadname << "thread id: " << pthread_self() << std::endl;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread 1");

    while (true)
    {
        std::cout << "I am main thread" << "thread id: " << pthread_self()<< std::endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
可以看到main线程id和新线程id是不一样的

pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址
在这里插入图片描述

上图左边是进程创建的地址空间,中间的mmap区域就是我们的共享区,里面便是Pthread库,右边就是Pthread库里有两个线程结构体图

4. 线程终止

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

  1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
  2. 线程可以调用pthread_ exit终止自己。
  3. 一个线程可以调用pthread_ cancel终止同一进程中的另一个线程。

4.1 pthread_exit()

线程终止函数接口

参数

  • value_ptr : value_ptr不要指向一个局部变量。
  • 返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)

测试代码

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>

// 新线程
void *ThreadRoutine(void *arg)
{
    const char *threadname = (const char *)arg;
    int cnt = 5;
    while (cnt--)
    {
        std::cout << "I am a new thread: " << threadname << std::endl;
        sleep(1);
    }
    pthread_exit(nullptr);
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread 1");

    while (true)
    {
        std::cout << "I am main thread" << std::endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述
可以看到我们的主线程还在执行输出语句,新线程已经结束生命周期了

4.2 pthread_cancel()

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>

// 新线程
void *ThreadRoutine(void *arg)
{
    const char *threadname = (const char *)arg;
    while (1)
    {
        std::cout << "I am a new thread: " << threadname << std::endl;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread 1");
    sleep(5);
    while (true)
    {
        std::cout << "I am main thread" << std::endl;
        sleep(1);
        pthread_cancel(tid);
    }
    return 0;
}

我们让主线程沉睡了5秒,5秒后新线程成功关闭
在这里插入图片描述


5.线程等待

  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
  • 创建新的线程不会复用刚才退出线程的地址空间。

线程等待没有之前的僵尸进程明显,但是每个线程创建后是需要我们对其进行回收的

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

在这里插入图片描述

测试代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

void *thread1(void *arg)
{
    printf("thread 1 returning ... \n");
    int *p = (int *)malloc(sizeof(int));
    *p = 1;
    return (void *)p;
}

void *thread2(void *arg)
{
    printf("thread 2 exiting ...\n");
    int *p = (int *)malloc(sizeof(int));
    *p = 2;
    pthread_exit((void *)p);
}

void *thread3(void *arg)
{
    while (1)
    { //
        printf("thread 3 is running ...\n");
        sleep(1);
    }
    return NULL;
}
int main(void)
{
    pthread_t tid;
    void *ret;

    // thread 1 return
    pthread_create(&tid, NULL, thread1, NULL);
    pthread_join(tid, &ret);
    printf("thread return, thread id %d, return code:%d\n",(int16_t)tid, *(int *)ret);
    free(ret);

    // thread 2 exit
    pthread_create(&tid, NULL, thread2, NULL);
    pthread_join(tid, &ret);
    printf("thread return, thread id %d, return code:%d\n", (int16_t)tid, *(int *)ret);
    free(ret);

    // thread 3 cancel by other
    pthread_create(&tid, NULL, thread3, NULL);
    sleep(3);
    pthread_cancel(tid);
    pthread_join(tid, &ret);
    if (ret == PTHREAD_CANCELED)
        printf("thread return, thread id %d, return code:PTHREAD_CANCELED\n", (int16_t)tid);
    else
        printf("thread return, thread id %d, return code:NULL\n", (int16_t)tid);

    return 0;
}

在这里插入图片描述


6. 分离线程

默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。

可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:

joinable和分离是冲突的,一个线程不能既是joinable又是分离的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

void *thread_run(void *arg)
{
    pthread_detach(pthread_self());
    printf("%s\n", (char *)arg);
    return NULL;
}

int main(void)
{
    pthread_t tid;
    if (pthread_create(&tid, NULL, thread_run,(void *)"thread1 run...") != 0)
    {
        printf("create thread error\n");
        return 1;
    }
    
    int ret = 0;
    sleep(1); // 很重要,要让线程先分离,再等待
    if (pthread_join(tid, NULL) == 0)
    {
        printf("pthread wait success\n");
        ret = 0;
    }
    else
    {
        printf("pthread wait failed\n");
        ret = 1;
    }
    return ret;
}

在这里插入图片描述


7. 拓展实验–给线程传入结构体

线程的第一个参数是他的线程ID,第二个是它的属性一般设置为nullptr,第三个为执行的函数,第四个参数则是可以传入线程的参数,其类型是void*的类型,所以我们可以尝试传入任意类型

#include <iostream>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <functional>
#include <string>
#include <ctime>
// typedef std::function<void()> func_t;
using func_t = std::function<void()>;

class ThreadData
{
public:
    ThreadData(const std::string &name, const uint64_t &ctime, func_t f)
        : threadname(name), createtime(ctime), func(f)
    {
    }

public:
    std::string threadname;
    uint64_t createtime;
    func_t func;
};

void Print()
{
    std::cout << "我是线程执行的大任务的一部分" << std::endl;
}

// 新线程
void *ThreadRountine(void *args)
{
    ThreadData *td = static_cast<ThreadData *>(args);
    while (true)
    {
        std::cout << "new thread"
                  << " thread name: " << td->threadname << " create time: " << td->createtime << std::endl;
        td->func();
        sleep(1);
    }
}
// 获取返回值
// 主线程
int main()
{

    pthread_t tid;
    ThreadData *td = new ThreadData("new thread", (uint64_t)time(nullptr), Print);
    pthread_create(&tid, nullptr, ThreadRountine, td);

    while (true)
    {
        std::cout << "main thread" << std::endl;
        sleep(3);
    }
}

在这里插入图片描述


8. 实现多线程

#include <iostream>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <functional>
#include <string>
#include <ctime>
// typedef std::function<void()> func_t;
using func_t = std::function<void()>;

const int threadnum = 5;

//给线程传入的结构体
class ThreadData
{
public:
    ThreadData(const std::string &name, const uint64_t &ctime, func_t f)
        : threadname(name), createtime(ctime), func(f)
    {
    }

public:
    std::string threadname;
    uint64_t createtime;
    func_t func;
};


void Print()
{
    std::cout << "我是线程执行的大任务的一部分" << std::endl;
}

// 新线程
void *ThreadRountine(void *args)
{
    int a = 10;
    ThreadData *td = static_cast<ThreadData *>(args); //类型转换为结构体指针
    while (true)
    {
        std::cout << "new thread"
                  << " thread name: " << td->threadname << " create time: " << td->createtime << std::endl;
        td->func();
        sleep(1);
    }
}

int main()
{
    std::vector<pthread_t> pthreads;
    for (size_t i = 0; i < threadnum; i++)
    {
    	//snprintf函数实现输入线程名称
        char threadname[64];
        snprintf(threadname, sizeof(threadname), "%s-%lu", "thread", i); 

        pthread_t tid;
        //time时间戳函数
        ThreadData *td = new ThreadData(threadname, (uint64_t)time(nullptr), Print);
        pthread_create(&tid, nullptr, ThreadRountine, td);
        pthreads.push_back(tid);
        sleep(1);
    }



    while (true)
    {
        std::cout << "main thread" << std::endl;
        sleep(3);
    }
}

在这里插入图片描述
可以看到这里右边有了六个线程,一个是我们的主线程。另外五个则是新创建的线程

举报

相关推荐

0 条评论