【Linux】多线程-线程同步

本文介绍了进程和线程的概念,包括进程的三种状态和线程的分类。在Linux系统中,线程被视为特殊的进程。通过示例代码展示了如何使用pthread库创建和管理线程,以及如何使用线程同步机制,如信号量、互斥锁、读写锁和条件变量,解决并发执行中的数据竞争问题。强调了线程同步的重要性,以确保程序的正确性。
摘要由CSDN通过智能技术生成

进程和线程

进程:一个正在运行的程序。

状态:就绪,运行,阻塞;

线程是进程中的一个执行路径,一个进程中至少有一个主线程(main函数);

有多条执行路径为多线程。

线程种类

三种线程

纯粹的用户级(并发)、存粹的内核级(并行)、组合(可用多处理器的资源,用户空间也可创建多个线程)。

操作系统用内核级

Linux把所有线程当作进程来实现、线程仅仅被视为一个与其他进程共享某些资源的进程(如地址空间),每个线程都有一个自己的PCB(进程控制块,task_struct);

创建一个线程

用pthread_create()创建

记得编译的时候后面加-pthread

函数pthread_join用来等待一个线程的结束,线程间同步的操作。

多个线程用同一个进程的地址空间

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
void *fun(void *arg)//这是一个线程
{
    for(int i=0;i<5;i++)
    {
        printf(“fun run\n”);
        sleep(1);
}
pthread_exit(“fun over”);//可以返回线程信息给主线程
}
int main()
{
    pthread_t id;
//create执行一次创建一个进程
    pthread_create(&id,NULL,fun,NULL);//创建了一个fun线程

    for(int i=0;i<5;i++)
    {
        printf(“main run\n”);
        sleep(1);
}
char *s = NULL;

pthread_join(id,(void**)&s);
//等待线程返回的信息,如果子线程没有运行完,主线程会被阻塞,可以防止fun线程没有运行完main就结束。
printf(“s=%s\n”,s);//不能用临时变量
exit(0);
}

两个路径同时进行,主函数(主进程)退出,其余进程也会结束,尽量让主函数慢点退出,sleep一下。

当然,用了pthread_join();接收返回信息阻塞可以不用sleep

了解程序的并发运行

并行是特殊的并发,我的虚拟机有4个处理器,所以准确来说这几个线程应该是并行的。

同时创建多个线程并打印自己是第几个线程:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
void *fun(void *arg)//这是一个线程
{
    int index = *(int*)arg;
    printf(“index=%d\n”,index);
sleep(1);
    printf(“index=%d\n”,index);
pthread_exit(NULL);//可以返回线程信息给主线程
}
int main()
{
    pthread_t id[5];
//create执行一次创建一个进程
int i=0;
    for(i=0;i<5;i++)
{
        pthread_create(&id[i],NULL,fun,(void*)&i);//创建线程,传地址
}
for(i=0;i<5;i++)
{
    pthread_join(id[i],NULL);
}

exit(0);
}

运行结果:

程序运行有不确定性(少用多线程)

因为线程获取i是靠i的地址,可能第一次循环的时候这些线程还没来得及获取i的值,到第二个for循环i被置0时才取得i的值,所以线程输出全是0。

获取i的值的速度差不多(同时获取值出现几个相同的),写入缓冲区的速度不一样不一定从0到4。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
int g_count = 1;
void *fun(void *arg)//这是一个线程
{
    for(int i=0;i<1000;i++)
    {
        printf(“g_count=%d\n”,g_count++);
    }
pthread_exit(NULL);//可以返回线程信息给主线程
}
int main()
{
    pthread_t id[5];
//create执行一次创建一个进程
    for(int i=0;i<5;i++)
{
        pthread_create(&id[i],NULL,fun,NULL);//创建线程,传地址
}
for(int i=0;i<5;i++)
{
    pthread_join(id[i],NULL);
}

exit(0);
}

理想的结果应该是5000,每个线程加1000次,但是结果会出现4999,4998等,这是因为两个进程同时执行了++,然后把数值存回去,本来应加两次变成了加一次,导致结果变小了。


线程同步

控制程序的执行速度或流程,保证程序的正确性。

线程同步方法:(线程间)信号量、互斥锁、条件变量、读写锁

用同步解决上面结果<5000的问题:

信号量:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>//信号量引用
int g_count = 1;
sem_t sem;//信号量变量

void *fun(void *arg)
{
    for(int i=0;i<1000;i++)
    {
        sem_wait(&sem);//p
        printf(“g_count=%d\n”,g_count++);
        sem_post(&sem);//v
    }
    pthread_exit(NULL);
}
int main()
{
    sem_init(&sem,0,1);//初始化sem=1
    pthread_t id[5];
    for(int i=0;i<5;i++)
    {
        pthread_create(&id[i],NULL,fun,NULL);//创建线程,传地址
    }
    for(int i=0;i<5;i++)
    {
        pthread_join(id[i],NULL);
    }
    sem_destory(&sem);//销毁
    exit(0);
}

互斥锁:

互斥锁:pthread_mutex_t

加锁的程序性能会下降

自己用时先加锁,此时别人用不了(别人无法加锁:线程被阻塞),自己用完再解锁。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>//互斥锁引用
int g_count = 1;
pthread_mutex_t mutex;//互斥锁

void *fun(void *arg)
{
    for(int i=0;i<1000;i++)
    {
        pthread_mutex_lock(&mutex);//加锁
        printf(“g_count=%d\n”,g_count++);
        pthread_mutex_unlock(&mutex);//解锁
    }
    pthread_exit(NULL);
}
int main()
{
    pthread_mutex_init(&mutex,NULL);//初始化
    pthread_t id[5];
    for(int i=0;i<5;i++)
    {
        pthread_create(&id[i],NULL,fun,NULL);
    }
    for(int i=0;i<5;i++)
    {
        pthread_join(id[i],NULL);
    }
    pthread_mutex_destroy(&mutex);//销毁锁
    exit(0);
}

读写锁:

读写锁数据类型pthread_rwlock_t

接口:

#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *rwlock,pthread_rwlockattr_t *attr);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t*rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
pthread_rwlock_t lock;
void* thread_w_fun(void*arg)
{
    for(int i=0;i<10;i++)
    {
        pthread_rwlock_wrlock(&lock);
        printf("-----------thread-w-start\n");
        sleep(3);
        printf("-----------thread-w-end\n");
        pthread_rwlock_unlock(&lock);
        sleep(1);
    }
}
void*thread_r_fun1(void*arg)
{
    for(int i=0;i<10;i++)
    {
        pthread_rwlock_rdlock(&lock);
        printf("fun1 r start\n");
        sleep(1);
        printf("fun1 r end\n");
        pthread_rwlock_unlock(&lock);
        sleep(1);
    }
}
void*thread_r_fun2(void*arg)
{
    for(int i=0;i<10;i++)
    {
        pthread_rwlock_rdlock(&lock);
        printf("fun2 r start\n");
        sleep(2);
        printf("fun2 r end\n");
        pthread_rwlock_unlock(&lock);
        sleep(1);
    }
}

int main()
{
    pthread_rwlock_init(&lock,NULL);//初始化
    pthread_t id1,id2,id3;//创建三个id接收读写锁的线程id
    pthread_create(&id1,NULL,thread_r_fun1,NULL);//创建读锁1线程
    pthread_create(&id2,NULL,thread_r_fun2,NULL);//读锁2
    pthread_create(&id3,NULL,thread_w_fun,NULL);//写锁

    pthread_join(id1,NULL);
    pthread_join(id2,NULL);
    pthread_join(id3,NULL);

    pthread_relock_destroy(&lock);//销毁锁
    exit(0);
}

多个读(或多个写)可以同时运行,一旦开始写就不能读,同理,一旦读就不能写了。

条件变量

条件变量:

条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值的时候,唤醒等待

这个共享数据的线程。

使用头文件:#include <pthread.h>

//初始化:
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);
//阻塞,把线程放条件变量队列中
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
//唤醒单个线程
int pthread_cond_signal(pthread_cond_t *cond);
//唤醒所有等待的线程
int pthread_cond_broadcast(pthread_cond_t *cond); 
//销毁
int pthread_cond_destroy(pthread_cond_t *cond);

注意:阻塞的时候必须要在前面加锁,后面解锁。

使用示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
char buff[128] = {0};

pthread_mutex_t mutex;//互斥锁
pthread_cond_t cond;//条件变量
//线程a
void*funa(void* arg)
{
    printf("aa start\n");
    while(1)
    {
        pthread_mutex_lock(&mutex);//加锁
        pthread_cond_wait(&cond,&mutex);//阻塞,把线程放条件变量队列中
        pthread_mutex_unlock(&mutex);//解锁

        if(strncmp(buff,"end",3)==0)//如果输入的是end,结束线程
        {
            break;
        }
        printf("funa:%s",buff);
    }
    printf("funa over\n");
}
//线程b
void*funb(void* arg)
{
    printf("bb start\n");
    while(1)
    {
        pthread_mutex_lock(&mutex);//加锁
        pthread_cond_wait(&cond,&mutex);//阻塞,(解锁,加锁)
        pthread_mutex_unlock(&mutex);//解锁

        if(strncmp(buff,"end",3)==0)
        {
            break;
        }
        printf("funb:%s",buff);
    }
    //唤醒时不希望有内容进出
    printf("funb over\n");
}
//线程c
void*func(void* arg)
{
    printf("cc start\n");
    while(1)
    {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond,&mutex);//阻塞,把线程放条件变量队列中
        pthread_mutex_unlock(&mutex);

        if(strncmp(buff,"end",3)==0)
        {
            break;
        }
        printf("func:%s",buff);
    }
    printf("func over\n");
}
//主函数
int main()
{
//初始化
    pthread_mutex_init(&mutex,NULL);//互斥锁
    pthread_cond_init(&cond,NULL);//条件变量
//创建线程
    pthread_t id1,id2,id3;
    pthread_create(&id1,NULL,funa,NULL);
    pthread_create(&id2,NULL,funb,NULL);
    pthread_create(&id3,NULL,func,NULL);
//同步数据
    while(1)
    {
        char tmp[128] = {0};
        fgets(tmp,128,stdin);

        strcpy(buff,tmp);//写入数据

        if(strncmp(tmp,"end",3)==0)//如果输入end,唤醒所有线程,使其结束
        {//唤醒所有线程
            pthread_cond_broadcast(&cond);
            break;
        }
        else//否则唤醒任意线程,同步数据
        {//唤醒任意线程
            pthread_cond_signal(&cond);
        }
    }
//等待线程结束
    pthread_join(id1,NULL);
    pthread_join(id2,NULL);
    pthread_join(id3,NULL);
//结束
    exit(0);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

曦樂~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值