[Linux]线程与同步机制

mac2025-06-01  34

一、单线程

1.1 源码

#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <stdlib.h> #include <errno.h> void sleep_us(unsigned long uSec){ struct timeval tv; tv.tv_sec=uSec/1000000; tv.tv_usec=uSec%1000000; int err; do{ err=select(0,NULL,NULL,NULL,&tv); }while(err<0 && errno==EINTR); } void* route(void *arg) { char* thread_name = (char*)arg; while(1){ if(ticket > 0){ printf("thread:%s sells ticket:%d\n",thread_name,ticket); ticket--; sleep_us(1000000); }else{ break; } } } void test_single_thread(){ pthread_t t1; pthread_create(&t1,NULL,route,(void*)"thread_1"); pthread_join(t1,NULL); } int main(){ test_single_thread(); return 0; }

1.2 结果

thread:thread_1 sells ticket:10 thread:thread_1 sells ticket:9 thread:thread_1 sells ticket:8 thread:thread_1 sells ticket:7 thread:thread_1 sells ticket:6 thread:thread_1 sells ticket:5 thread:thread_1 sells ticket:4 thread:thread_1 sells ticket:3 thread:thread_1 sells ticket:2 thread:thread_1 sells ticket:1

二、多线程

2.1 源码

#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <stdlib.h> #include <errno.h> int ticket = 10; void sleep_us(unsigned long uSec){ struct timeval tv; tv.tv_sec=uSec/1000000; tv.tv_usec=uSec%1000000; int err; do{ err=select(0,NULL,NULL,NULL,&tv); }while(err<0 && errno==EINTR); } void* route(void *arg) { char* thread_name = (char*)arg; while(1){ if(ticket > 0){ printf("thread:%s sells ticket:%d\n",thread_name,ticket); ticket--; sleep_us(1000000); }else{ break; } } } void test_multi_thread(){ pthread_t t1; pthread_t t2; pthread_create(&t1,NULL,route,(void*)"thread_1"); pthread_create(&t2,NULL,route,(void*)"thread_2"); pthread_join(t1,NULL); pthread_join(t2,NULL); } int main(){ test_multi_thread(); return 0; }

2.2 结果

thread:thread_2 sells ticket:10 thread:thread_1 sells ticket:10 thread:thread_1 sells ticket:8 thread:thread_2 sells ticket:8 thread:thread_2 sells ticket:6 thread:thread_1 sells ticket:6 thread:thread_1 sells ticket:4 thread:thread_2 sells ticket:4 thread:thread_2 sells ticket:2 thread:thread_1 sells ticket:2 thread:thread_2 sells ticket:0

错误结果:线程1和线程2同时卖同一票

 

三、多线程与同步机制

3.1 互斥锁(mutex)

3.1.1 函数

初始化锁

    静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

    动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr)

加锁

    int pthread_mutex_lock(pthread_mutex *mutex)

    int pthread_mutex_trylock(pthread_mutex_t *mutex)

解锁

    int pthread_mutex_unlock(pthread_mutex_t *mutex)

销毁锁

    int pthread_mutex_destroy(pthread_mutex *mutex)

3.1.2 源码

#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <stdlib.h> #include <errno.h> pthread_mutex_t mutex; //定义 int ticket = 10; void sleep_us(unsigned long uSec){ struct timeval tv; tv.tv_sec=uSec/1000000; tv.tv_usec=uSec%1000000; int err; do{ err=select(0,NULL,NULL,NULL,&tv); }while(err<0 && errno==EINTR); } void* route_mutex(void *arg) { char* thread_name = (char*)arg; while(1){ pthread_mutex_lock(&mutex); if(ticket > 0){ printf("thread:%s sells ticket:%d\n",thread_name,ticket); ticket--; pthread_mutex_unlock(&mutex); sleep_us(1000000);//注意休眠的位置 }else{ pthread_mutex_unlock(&mutex); break; } } } void test_multi_thread_mutex(){ pthread_t t1; pthread_t t2; pthread_mutex_init(&mutex,NULL); //初始化锁 pthread_create(&t1,NULL,route_mutex,(void*)"thread_1"); pthread_create(&t2,NULL,route_mutex,(void*)"thread_2"); pthread_join(t1,NULL); pthread_join(t2,NULL); pthread_mutex_destroy(&mutex);//销毁锁 } int main(){ test_multi_thread_mutex(); return 0; }

3.1.3 结果

thread:thread_1 sells ticket:10 thread:thread_2 sells ticket:9 thread:thread_2 sells ticket:8 thread:thread_1 sells ticket:7 thread:thread_2 sells ticket:6 thread:thread_1 sells ticket:5 thread:thread_2 sells ticket:4 thread:thread_1 sells ticket:3 thread:thread_1 sells ticket:2 thread:thread_2 sells ticket:1

正确结果:某一张票只能由线程1或者线程2卖出

 

3.2 条件变量(cond)

3.2.1 函数

初始化条件变量

    静态态初始化,pthread_cond_t cond = PTHREAD_COND_INITIALIER

    动态初始化,int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)

等待条件成立(释放锁,同时阻塞等待条件变量为真才行)

    int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)

    int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime)

唤醒条件变量

    int pthread_cond_signal(pthread_cond_t *cond)

    int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞

清除条件变量

    int pthread_cond_destroy(pthread_cond_t *cond)

3.2.2 源码

#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <stdlib.h> #include <errno.h> pthread_mutex_t mutex; pthread_cond_t cond; //定义 int ticket = 10; void sleep_us(unsigned long uSec){ struct timeval tv; tv.tv_sec=uSec/1000000; tv.tv_usec=uSec%1000000; int err; do{ err=select(0,NULL,NULL,NULL,&tv); }while(err<0 && errno==EINTR); } void* route_consume_cond(void *arg) { char* thread_name = (char*)arg; while(1){ pthread_mutex_lock(&mutex); //上锁 if(ticket <= 0){ printf("thread:%s has ticket:%d,waiting...\n",thread_name,ticket); pthread_cond_wait(&cond,&mutex);//条件不成立时挂起等待 } if(ticket > 0){ printf("thread:%s sells ticket:%d\n",thread_name,ticket); ticket--; //修改 } pthread_mutex_unlock(&mutex); //解锁 sleep_us(1000000); } } void* route_produce_cond(void *arg) { char* thread_name = (char*)arg; while(1){ pthread_mutex_lock(&mutex); //上锁 printf("thread:%s sells ticket:%d\n",thread_name,ticket); ticket++; pthread_cond_signal(&cond); //唤醒 pthread_mutex_unlock(&mutex); //解锁 sleep_us(1200000); } } void test_multi_thread_available(){ pthread_t t1; pthread_t t2; pthread_mutex_init(&mutex,NULL); pthread_cond_init(&cond,NULL); pthread_create(&t1,NULL,route_consume_cond,(void*)"consume"); pthread_create(&t2,NULL,route_produce_cond,(void*)"produce"); pthread_join(t1,NULL); pthread_join(t2,NULL); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); } int main(){ test_multi_thread_available(); return 0; }

3.2.3 结果

thread:consume sells ticket:10 thread:produce sells ticket:10 thread:consume sells ticket:10 thread:produce sells ticket:9 thread:consume sells ticket:10 thread:produce sells ticket:9 thread:consume sells ticket:10 ... thread:consume sells ticket:1 thread:produce sells ticket:0 thread:consume sells ticket:1 thread:produce sells ticket:0 thread:consume sells ticket:1 thread:produce sells ticket:0 thread:consume sells ticket:1 thread:produce sells ticket:0 thread:consume sells ticket:1

正常结果:(案例是:窗口票卖出快,窗口进票慢)最后出现,只有等到produce线程进了1正票之后,consume线程才能再卖出1张

3.2.4 pthread_cond_wait 参数二

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

cond: 要在这个条件变量上等待(把当前线程挂起)mutex: 互斥量(把当前线程拿到的锁释放)

为什么第二个参数要传互斥量? 因为等待条件满足是在临界区里等待的,如果某个线程直接进行等待,那么就会抱着锁去等待,此时,其他线程也无法进入临界资源。

剖析:pthread_cond_wait临时将当前线程拿到的mutex释放,允许其他线程占有mutex,不必等待.

这里route_consume_cond通过pthread_mutex_lock先占有mutex,之后通过pthread_cond_wait等待条件变量但会先临时释放mutex,使得其他线程可临时能占有锁;

这样,route_produce_cond线程到达pthread_mutex_lock时,是能占有mutex,不必等待.之后会pthread_cond_signal唤醒条件变量.

3.2.5 条件变量使用规范

(1)等待条件

pthread_mutex_lock(&lock);        #上锁 while(条件不成立) {     pthread_cond_wait(&cond);    #条件不成立时挂起等待(注意是while) } 进行操作......                               #操作 pthread_mutex_unlock(&lock);   #解锁

(2)给条件发信号

pthread_mutex_lock(&lock);       #上锁 设置条件为真...                          #符合条件 pthread_cond_signal(&cond);    #唤醒 pthread_mutex_unlock(&lock);   #解锁  

3.3 信号量(sem)

3.3.1 函数

信号量初始化

    int sem_init (sem_t *sem , int pshared, unsigned int value)

    #sem:指定的信号量进行初始化,

    #pshared:表示此信号量是在进程间共享还是线程间共享,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部        信号量),

    #value:信号量的初始值

等待信号量

    int sem_wait(sem_t *sem)

    #如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞

释放信号量。信号量值加1。并通知其他等待线程

    int sem_post(sem_t *sem)

销毁信号量

    int sem_destroy(sem_t *sem)

3.3.2 源码

#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <stdlib.h> #include <errno.h> #include <semaphore.h> sem_t sem[2]; int ticket = 10; void* route_consume_sem(void *arg) { char* customer_name = (char*)arg; while(1){ sem_wait(&sem[0]);//add lock for sem[0] ticket--; printf("thread:%s consume ticket:%d\n",customer_name,ticket); sem_post(&sem[1]);//release lock for sem[0] sleep_us(1000000); } } void* route_produce_sem(void *arg) { char* customer_name = (char*)arg; while(1){ sem_wait(&sem[1]); //add lock for sem[1] ticket++; printf("thread:%s produce ticket:%d\n",customer_name,ticket); sem_post(&sem[0]); //release lock for sem[0] sleep_us(1000000); } } void test_multi_thread_sem(){ pthread_t t1; pthread_t t2; sem_init(&sem[0],0,0); //For consume thread, init value is 0 sem_init(&sem[1], 0, 1);//For produce thread, init value is 1 // pthread_create(&t1,NULL,route_consume_sem,(void*)"consume"); pthread_create(&t2,NULL,route_produce_sem,(void*)"produce"); pthread_join(t1,NULL); pthread_join(t2,NULL); sem_destroy(&sem[0]); sem_destroy(&sem[1]); } int main(){ test_multi_thread_sem(); return 0; }

3.3.3 结果

thread:produce produce ticket:11 thread:consume consume ticket:10 thread:produce produce ticket:11 thread:consume consume ticket:10 thread:produce produce ticket:11 thread:consume consume ticket:10 thread:produce produce ticket:11 thread:consume consume ticket:10

3.4 区别

[参考:https://blog.csdn.net/a987073381/article/details/52029070]

互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。 同步:主要是流程上的概念,是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的

(1)互斥锁:互斥,一个线程占用了某个资源,那么其它的线程就无法访问,直到这个线程解锁,其它线程才可以访问

(2)条件变量:同步,一个线程完成了某一个动作就通过条件变量发送信号告诉别的线程,别的线程再进行某些动作

(3)信号量:同步,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动

 

附Makefile:

##dir PWD_DIR=$(shell pwd) OBJ_DIR=$(PWD_DIR)/obj BIN_DIR=$(PWD_DIR)/bin ##compiler CC=gcc ##target TARGET=e_pthread ##build $(TARGET):main.o $(CC) main.o -o $@ -lpthread main.o:main.cpp $(CC) -c $^ clean: rm -rf *.o $(TARGET)

 

 

最新回复(0)