Linux多线程编程

线程创建

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);

功能:创建子线程
参数:
    - pthread_t *thread:线程号,传出参数,当线程创建成功后会赋予该值一个线程号
    - const pthread_attr_t *attr:线程属性
    - void *(*start_routine) (void *):线程函数,该参数类型为函数指针,表示要执行的子线程函数
    - void *arg:为第三个参数使用

代码示例

#include<pthread.h>
#include<iostream>
using namespace std;
​
void *callback(void *args)
{
    //子线程执行代码
    //在子线程中输出自己的线程号
    cout<<"child tid:"<<pthread_self()<<endl;
    return NULL;
}
​
int main()
{
    pthread_t tid;//传出参数
    pthread_create(&tid,NULL,callback,NULL);
    //主线程执行
    int n=5;
    for(int i=0;i<n;i++)
        cout<<i<<endl;
    //在主线程中输出子线程的线程号
    cout<<"main thread:"<<pthread_self()<<"\tmain child tid"<<tid<<endl;
    //主线程等待子线程执行完毕后回收子线程资源
    pthread_join(tid,NULL);
    return 0;
}

注:主函数就是主线程

线程终止

pthread_exit() 是一个线程终止函数,用于在线程内部终止当前线程的执行。它可以带有一个可选的返回值指针作为参数,用于向调用线程传递退出状态。

函数原型:

#include <pthread.h>
​
void pthread_exit(void *retval);

参数:

  • retval:可选的返回值指针,表示线程的退出状态。默认情况下,retval 被设置为 0。

说明:

  • pthread_exit() 函数会立即终止当前线程的执行,不会返回到调用者,并且不会执行任何清理操作。

  • 如果线程有被动分离属性,那么调用 pthread_exit() 函数将自动释放该线程占用的资源。

  • 如果线程没有被分离或销毁,而是等待其他线程调用 pthread_join() 获取退出状态,那么 pthread_exit() 会保持线程处于静止状态,直到其他线程调用 pthread_join() 获取退出状态或者进程终止。

示例:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
​
void *thread_func(void *arg) {
    printf("子线程开始执行...\n");
    pthread_exit(NULL); // 终止当前线程的执行
    printf("子线程执行结束...\n"); // 这行代码不会被执行
    return NULL;
}
​
int main() {
    pthread_t thread_id;
    int ret = pthread_create(&thread_id, NULL, thread_func, NULL);
    if (ret != 0) {
        perror("pthread_create");
        exit(1);
    }
    pthread_join(thread_id, NULL); // 等待子线程执行完毕
    printf("主线程结束...\n");
    return 0;
}

在上面的示例中,主线程创建了一个子线程,并调用 pthread_join() 等待子线程结束。而在子线程内部,使用 pthread_exit() 终止了子线程的执行。最终程序会输出"主线程结束...",而不会输出"子线程执行结束...",因为 pthread_exit 终止了子线程的执行。

线程连接

 #include <pthread.h>
​
 int pthread_join(pthread_t thread, void **retval);

功能:用于等待线程号为thread的线程结束,并获取其退出状态,调用该函数的线程将被阻塞,直到线程号为thread的线程执行完毕

参数:

  • thread:要等待的线程标识符。

  • retval:用于存储被等待线程的退出状态的指针。

返回值:

  • 若成功,返回 0。

  • 若失败,返回错误码。

说明:

  • 调用 pthread_join() 函数会使当前线程阻塞,直到指定的线程结束为止。

  • 如果被等待的线程已经结束,pthread_join() 函数立即返回,可以通过 retval 参数获取其退出状态。

  • 被等待的线程必须是可连接的,否则调用将会出错。可连接的线程通常是通过 pthread_create() 创建的。

  • 当一个线程结束时,它的退出状态由线程返回值决定,该返回值可以通过 retval 指针传递给 pthread_exit() 或从线程函数显式返回。

  • 如果不关心被等待线程的退出状态,可以将 retval 参数设置为 NULL

  • 每个线程只能被 join() 一次,所以多次调用 pthread_join() 会导致错误。

代码示例

#include<pthread.h>
#include<stdio.h>
#include<iostream>
#include<unistd.h>
using namespace std;
​
void *callback(void *args)
{
    cout<<"child pid:"<<pthread_self()<<endl;
​
    static int value=10;
    sleep(3);
    return (void *)&value;
}
​
​
int main()
{
    pthread_t pid;
    pthread_create(&pid,NULL,callback,NULL);
​
    int n=5;
    for(int i=0;i<n;i++)
        cout<<i<<endl;
​
    cout<<"main pid:"<<pthread_self()<<"\t main child pid"<<pid<<endl;
​
    int *val=0;
    pthread_join(pid,(void **)&val);
    cout<<*val<<endl;
    //pthread_exit(NULL);
​
    return 0;
}
​
​

线程分离

pthread_detach() 函数将一个线程标记为分离状态,使得该线程终止时自动释放所占用的资源。

函数原型:

#include <pthread.h>
​
int pthread_detach(pthread_t thread);

参数:

  • thread:要分离的线程标识符。

返回值:

  • 若成功,返回 0。

  • 若失败,返回错误码。

说明:

  • 调用 pthread_detach() 函数将一个线程标记为分离状态,使得该线程终止时自动释放所占用的资源,无需其他线程调用 pthread_join() 获取其退出状态。

  • 如果线程已经是分离状态,或者已经被销毁,则该函数什么也不做。

  • 可连接的线程不应该使用 pthread_exit() 函数来终止线程,应该由其他线程调用 pthread_join 来等待并获取其退出状态;而分离的线程通常会使用 pthread_exit() 函数来终止自己的执行。

  • 一旦线程被标记为分离状态,就不能再将其恢复为可连接状态。

示例:

#include<pthread.h>
#include<iostream>
using namespace std;
​
void *callback(void *args)
{
    //子线程执行代码
    //在子线程中输出自己的线程号
    cout<<"child tid:"<<pthread_self()<<endl;
    return NULL;
}
​
int main()
{
    pthread_t tid;//传出参数
    pthread_create(&tid,NULL,callback,NULL);
    //主线程执行
    int n=5;
    for(int i=0;i<n;i++)
        cout<<i<<endl;
    //在主线程中输出子线程的线程号
    cout<<"main thread:"<<pthread_self()<<"\tmain child tid"<<tid<<endl;
    //设置子线程分离,此时子线程结束时不需要主线程回收资源
    pthread_detach(tid);
    pthread_exit(NULL);
    return 0;
}

线程同步

    ​
#include<unistd.h>
#include<pthread.h>
#include<iostream>
using namespace std;
​
int num=100;
//三个子线程同时使用该子线程函数,并访问全局变量
void *ticket(void *args)
{
    int thread_num=*(int *)args;
    while(num>0)
    {
        cout<<pthread_self()<<thread_num<<"号窗口卖票:"<<num<<endl;
        num--;
    }
    return NULL;
}
​
int main()
{
    pthread_t tid[3];
    //创建三个子线程
    for(int i=0;i<3;i++)
        pthread_create(&tid[i],NULL,ticket,(void*)&i);
​
    for(int i=0;i<3;i++)
        pthread_join(tid[i],NULL);
​
    pthread_exit(NULL);
​
    return 0;
}

​

互斥锁

#include<unistd.h>
#include<pthread.h>
#include<iostream>
using namespace std;

int num=100;
pthread_mutex_t mutex;//创建互斥量

//三个子线程同时使用该子线程函数,并访问全局变量
void *ticket(void *args)
{
    int thread_num=*(int *)args;
    pthread_mutex_lock(&mutex);
    while(1)
    {
        if(num>0)
        {
            cout<<pthread_self()<<"--"<<thread_num<<"号窗口卖票:"<<num<<endl;
            num--;
        }
        else
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main()
{
    //初始化互斥锁
    pthread_mutex_init(&mutex,NULL);
    pthread_t tid[3];
    //创建三个子线程
    for(int i=0;i<3;i++)
        pthread_create(&tid[i],NULL,ticket,(void*)&i);

    for(int i=0;i<3;i++)
        pthread_join(tid[i],NULL);

    pthread_exit(NULL);

    //销毁锁
    pthread_mutex_destroy(&mutex);
    return 0;
}

条件变量

生产者消费者模型

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;


typedef struct Node
{
    int num;
    struct Node* next;
}Node;

Node *head=NULL;

pthread_mutex_t mutex;

void *producer(void *args)
{
    while(1)
    {
        pthread_mutex_lock(&mutex);
        Node *p=new Node;
        p->num=rand()%100;
        p->next=head;
        head=p;
        cout<<pthread_self()<<" produce: "<<p->num<<endl;
        pthread_mutex_unlock(&mutex);
        usleep(100);

    }
    return NULL;
}


void *customer(void *args)
{
    while(1)
    {

        pthread_mutex_lock(&mutex);
        Node *tmp=head;
        if(head!=NULL)
        {

            head=head->next;
            cout<<pthread_self()<<" custome: "<<tmp->num<<endl;
            delete tmp;
            pthread_mutex_unlock(&mutex);
            usleep(100);

        }else{
            pthread_mutex_unlock(&mutex);
        }

    }
    return NULL;
}


int main()
{

    int pthread_num=5;
    pthread_t ptids[pthread_num],ctids[pthread_num];

    pthread_mutex_init(&mutex,NULL);

    for(int i=0;i<pthread_num;i++)
    {

        pthread_create(&ptids[i],NULL,producer,NULL);
        pthread_create(&ctids[i],NULL,customer,NULL);
    }

    for(int i=0;i<pthread_num;i++)
    {

        pthread_detach(ptids[i]);
        pthread_detach(ctids[i]);
    }

    //防止某一个子线程执行完毕后直接销毁掉锁,导致其他子线程无法使用锁
    while(1) sleep(10);

    pthread_mutex_destroy(&mutex);
    //退出主线程
    pthread_exit(NULL);

    return 0;
}

分析

在上述生产者消费者模型中,生产者不断生产新产品,消费者消费产品,当产品消耗完毕后,消费者将一直判断等待生产者生产,这消耗了消费者线程的资源,因此出现了条件变量的使用,当产品消耗完毕后,将消费者进行阻塞,此时等待生产者的生产信号之后解除阻塞进行消费

#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;


typedef struct Node
{
    int num;
    struct Node* next;
}Node;

Node *head=NULL;

//互斥锁
pthread_mutex_t mutex;
//条件变量
pthread_cond_t cond;

void *producer(void *args)
{
    while(1)
    {
        pthread_mutex_lock(&mutex);
        Node *p=new Node;
        p->num=rand()%100;
        p->next=head;
        head=p;
        cout<<pthread_self()<<" produce: "<<p->num<<endl;
        pthread_mutex_unlock(&mutex);

        pthread_cond_signal(&cond);//生产了一个产品之后就通知消费者进行消费
        usleep(100);

    }
    return NULL;
}


void *customer(void *args)
{
    while(1)
    {

        pthread_mutex_lock(&mutex);
        Node *tmp=head;
        if(head!=NULL)
        {

            head=head->next;
            cout<<pthread_self()<<" custome: "<<tmp->num<<endl;
            delete tmp;
            pthread_mutex_unlock(&mutex);
            usleep(100);

        }else{
            /*
              当没有产品可以消费,就阻塞当前线程,等待生产者生产数据的信号
              该函数可以在阻塞线程时对mutex进行解锁,
              当数据生产信号到达后解除阻塞时,再对mutex进行上锁
              */
            pthread_cond_wait(&cond,&mutex);
            pthread_mutex_unlock(&mutex);
        }

    }
    return NULL;
}


int main()
{

    int pthread_num=5;
    pthread_t ptids[pthread_num],ctids[pthread_num];

    pthread_mutex_init(&mutex,NULL);
    pthread_cond_init(&cond,NULL);
    for(int i=0;i<pthread_num;i++)
    {

        pthread_create(&ptids[i],NULL,producer,NULL);
        pthread_create(&ctids[i],NULL,customer,NULL);
    }

    for(int i=0;i<pthread_num;i++)
    {

        pthread_detach(ptids[i]);
        pthread_detach(ctids[i]);
    }

    //防止某一个子线程执行完毕后直接销毁掉锁,导致其他子线程无法使用锁
    while(1) sleep(10);

    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mutex);
    //退出主线程
    pthread_exit(NULL);

    return 0;
}

信号量

信号量的初始化

#include <semaphore.h>
​
int sem_init(sem_t *sem, int pshared, unsigned int value);

参数

  1. sem:指向要初始化的信号量的指针。需要提前声明一个 sem_t 类型的变量,然后将其地址传递给该参数。

  2. pshared:描述信号量的类型。它可以取两个值:

    • 如果 pshared 值为 0,则表示信号量是线程内部的,只能在同一进程的不同线程中共享。

    • 如果 pshared 值为非零(通常是 1),则表示信号量是由多个进程之间共享的。

  3. value:表示信号量的初始值。这个初始值应该是一个非负整数,用来表示可以同时执行的线程或者进程的数量。

代码示例
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<semaphore.h>
using namespace std;
​
​
typedef struct Node
{
    int num;
    struct Node* next;
}Node;
​
Node *head=NULL;
​
//互斥锁
pthread_mutex_t mutex;
//设置信号量
sem_t psem,csem;
​
void *producer(void *args)
{
    while(1)
    {
        //生产者接受到消费者信号后生产数据
        sem_wait(&psem);
        pthread_mutex_lock(&mutex);
        Node *p=new Node;
        p->num=rand()%100;
        p->next=head;
        head=p;
        cout<<pthread_self()<<" produce: "<<p->num<<endl;
        pthread_mutex_unlock(&mutex);
​
        //生产者生产完毕后通知消费者消费
        sem_post(&csem);
        usleep(100);
​
    }
    return NULL;
}
​
​
void *customer(void *args)
{
    while(1)
    {
        //消费者接受到生产的生产信号后开始消费数据
        sem_wait(&csem);
        pthread_mutex_lock(&mutex);
        Node *tmp=head;
​
        head=head->next;
        cout<<pthread_self()<<" custome: "<<tmp->num<<endl;
        delete tmp;
        pthread_mutex_unlock(&mutex);
        //消费者消耗数据之后给生产发信号生产数据
        sem_post(&psem);
        usleep(100);
​
    }
    return NULL;
}
​
​
int main()
{
​
    int pthread_num=5;
    pthread_t ptids[pthread_num],ctids[pthread_num];
​
    pthread_mutex_init(&mutex,NULL);
    //初始化信号量
    //第二个参数0表示该信号量用于线程,第三个参数表示该信号量的初始值
    sem_init(&psem,0,8);
    sem_init(&csem,0,0);
    for(int i=0;i<pthread_num;i++)
    {
​
        pthread_create(&ptids[i],NULL,producer,NULL);
        pthread_create(&ctids[i],NULL,customer,NULL);
    }
​
    for(int i=0;i<pthread_num;i++)
    {
​
        pthread_detach(ptids[i]);
        pthread_detach(ctids[i]);
    }
​
    //防止某一个子线程执行完毕后直接销毁掉锁,导致其他子线程无法使用锁
    while(1) sleep(10);
​
​
    sem_destroy(&psem);
    sem_destroy(&csem);
    pthread_mutex_destroy(&mutex);
    //退出主线程
    pthread_exit(NULL);
​
    return 0;
}
​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值