【Linux系统编程】线程之间的同步与协调
这里介绍一下如何使用线程来实现并发的功能,如何使用互斥锁或者信号量来实现线程同步,如何使用条件变量来实现多线程之间的通信,借助条件变量,可以实现线程之间的协调,使得各个线程能够按照特定的条件进行等待或唤醒。
目录
线程同步
现在我们有两个线程,都给全局变量counter增加5000次
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define NLOOP 5000
int counter;
void *increase(void *vptr);
int main(int argc, char **argv) {
pthread_t threadIdA, threadIdB;
pthread_create(&threadIdA, NULL, &increase, NULL);
pthread_create(&threadIdB, NULL, &increase, NULL);
pthread_join(threadIdA, NULL);
pthread_join(threadIdB, NULL);
return 0;
}
void *increase(void *vptr) {
int i, val;
for (i = 0; i < NLOOP; i++) {
val = counter;
printf("%x: %d\n", (unsigned int) pthread_self(), val + 1);
counter = val + 1;
}
return NULL;
}
?运行结果是这样的
?原程序会出现竟态条件,存在多个线程同时给共享变量赋值的情况,这会导致结果错误。
我们可以使用互斥锁或者信号量的同步机制来保证线程之间的同步,实际上,无论我们使用互斥锁还是信号量的处理方法,我们都会遇到一个问题,那就是究竟选择是在循环外加锁还是循环内加锁。
这种情况下,应该选择在循环内加锁。如果将锁放在循环外部,那么当一个线程获得锁并开始执行加法操作时,另一个线程必须等待,直到锁被释放,循环次数越多,线程间的等待就越久,并发性能严重下降。
互斥锁
互斥锁(Mutex)是一种用于多线程编程中的同步机制,用于保护共享资源,防止多个线程同时访问或修改同一资源而导致数据不一致或冲突。
互斥锁提供了两个基本操作:锁定(Lock)和解锁(Unlock)。当一个线程获得了互斥锁的锁定状态后,其他线程就无法立即获取该锁,只能等待锁被解锁后才能尝试获取。这样可以确保在任意时刻只有一个线程能够访问被保护的资源,从而避免了竞态条件和数据不一致的问题。
使用互斥锁时,需要在访问共享资源之前对互斥锁进行加锁操作,访问完毕后再进行解锁操作,这样可以保证在同一时间只有一个线程可以访问该资源。互斥锁可以防止多个线程同时修改共享资源,保证了线程安全性。
添加一个全局互斥锁,在主线程中初始化互斥锁,然后在操作完成后销毁互斥锁。
在每次对counter进行处理的时候都先加锁,在操作完成之后再解锁。
重新编译运行程序,可以得到想要的结果了。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define NLOOP 5000
int counter;
pthread_mutex_t mutex; // 互斥锁
void *increase(void *vptr) {
int i, val;
for (i = 0; i < NLOOP; i++) {
pthread_mutex_lock(&mutex); // 加锁
val = counter;
printf("%x: %d\n", (unsigned int)pthread_self(), val + 1);
counter = val + 1;
pthread_mutex_unlock(&mutex); // 解锁
}
return NULL;
}
int main(int argc, char **argv) {
pthread_t threadIdA, threadIdB;
pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
pthread_create(&threadIdA, NULL, &increase, NULL);
pthread_create(&threadIdB, NULL, &increase, NULL);
pthread_join(threadIdA, NULL);
pthread_join(threadIdB, NULL);
pthread_mutex_destroy(&mutex); // 销毁互斥锁
return 0;
}
信号量
信号量(Semaphore)是一种用于多线程编程中的同步机制,用于控制对共享资源的访问。
信号量维护了一个计数器,用于表示可用的资源数量。当线程需要访问共享资源时,首先会尝试对信号量进行P操作(也称为申请操作),该操作会将信号量的计数器减1。如果计数器大于等于0,则表示有可用资源,线程可以继续执行;如果计数器小于0,则表示没有可用资源,线程需要等待。当线程使用完共享资源后,会对信号量进行V操作(也称为释放操作),该操作会将信号量的计数器加1,表示释放了一个资源。
添加一个全局的信号量,在主线程中初始化信号量,并在操作完成后销毁信号量。
???? 然后在每次线程对counter处理之前都先等待信号量,申请资源,然后操作完成之后再发送信号量释放资源。
重新编译运行程序,可以得到想要的结果了。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define NLOOP 5000
int counter;
void *increase(void *vptr);
sem_t sem; // 信号量
int main(int argc, char **argv) {
pthread_t threadIdA, threadIdB;
sem_init(&sem, 0, 1); // 初始化信号量,初值为1
pthread_create(&threadIdA, NULL, &increase, NULL);
pthread_create(&threadIdB, NULL, &increase, NULL);
pthread_join(threadIdA, NULL);
pthread_join(threadIdB, NULL);
sem_destroy(&sem); // 销毁信号量
return 0;
}
void *increase(void *vptr) {
int i, val;
for (i = 0; i < NLOOP; i++) {
sem_wait(&sem); // 等待信号量
val = counter;
printf("%x: %d\n", (unsigned int) pthread_self(), val + 1);
counter = val + 1;
sem_post(&sem); // 发送信号量
}
return NULL;
}
线程协调通信
条件变量
条件变量是一种用于多线程编程中的同步机制,通常与互斥锁结合使用,用于在线程间进行通信和协调。
条件变量主要用于线程的等待和通知。当一个线程在某个条件下无法继续执行时,可以通过条件变量将自己挂起,等待其他线程的通知。另外,当某个条件得到满足时,线程可以向其他线程发送通知,唤醒等待的线程继续执行。
假设需要开4个线程,这4个线程的ID分别为p1、p2、p3、p4(请以真实线程id代替),每个线程将自己的ID在屏幕上打印5遍,要求输出结果必须按p1*p2**p3***p4****的模式显示;即:p1*p2**p3***p4**** p1*p2**p3***p4****…依次递推。
这里我们就需要让这四个线程之间协调工作
我们这里使用到互斥锁和条件变量,先在声明的时候初始化,同时需要一个全局变量来控制每个线程的输出顺序。
主函数创建了四个线程,并向每个线程传入了需要打印*的次数参数,这里使用了一个times数组而不是times整型变量,这是因为防止线程还没使用到正确的times值之前times又在下一次的循环中被修改了。
在线程执行的函数中,先将指针转换为整型指针然后拿到整数的值,循环5次,每次循环中先加锁,然后判断counter和4取余是否等于打印*的次数减一,即判断是否轮到该线程输出,如果不是轮到该线程输出,那么该线程就进入等待。
??? 在某个线程输出完之后,counter++,同时唤醒所有等待线程并解锁。
编译运行程序,可见我们编写的程序输出了正确的结果。
#include <stdio.h>
#include <pthread.h>
int counter = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void *new(void *vptr) {
int*p=(int*)vptr;
int times=*p;
for (int i = 0; i < 5; i++) {
pthread_mutex_lock(&mutex); // 加锁
while (counter%4 != times-1)
pthread_cond_wait(&cond, &mutex);
printf("%x", (unsigned int)pthread_self());
counter++;
for(int j=0;j<times;j++)
putchar('*');
if(times==4)
putchar('\n');
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex); // 解锁
}
return NULL;
}
int main(int argc, char **argv) {
pthread_t threads[4];
int times[4];
for (int i = 0; i < 4; i++){
times[i]=i+1;
pthread_create(&threads[i], NULL, &new, ×[i]);
}
for (int i = 0; i < 4; i++)
pthread_join(threads[i], NULL);
pthread_mutex_destroy(&mutex); // 销毁互斥锁
return 0;
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!