当前位置: 代码迷 >> 综合 >> 【Linux】线程同步
  详细解决方案

【Linux】线程同步

热度:40   发布时间:2024-02-05 04:36:07.0

线程/进程同步:解决线程/进程之间的竞争关系或者协作关系(线程中竞争关系会更多,因为线程数据共享会很多,会访问共享数据,访问共享数据会出现竞争)

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

一、互斥锁:

  • 操作的实体是锁,锁的状态有两种:加锁和解锁
  • 线程在进入临界区之前,加锁操作;如果锁是加锁状态,则执行加锁操作将被阻塞
  • 临界区就是共享资源的访问代码
  • 线程退出临界区之后,解锁操作
  • 互斥锁就是因为这把锁只允许一个线程进行加锁

1、互斥锁类型:

prhread_mutex_t;一般将锁定义到全局

2、 互斥锁操作:

(1)初始化

int pthread_mutex_init(prhread_mutex_t  *mutex,pthread_mutexattr_t  *attr);

  • prhread_mutex_t  *mutex:锁类型
  • pthread_mutexattr_t  *attr:锁的属性;默认属性为NULL

(2)加锁

int  pthread_mutex_lock(prhread_mutex_t  *mutex);//如果锁是加锁状态,则执行加锁操作将被阻塞

int  pthread_mutex_trylock(prhread_mutex_t  *mutex);//如果锁是加锁状态,则执行加锁操作不会被阻塞

(3)解锁

int  pthread_mutex_unlock(prhread_mutex_t  *mutex);

(4)销毁

int  pthread_mutex_destroy(prhread_mutex_t  *mutex);

 3、例题:主线程负责接收用户输入,函数线程负责将用户输入打印到终端界面(最好用信号量实现,因为互斥锁更多的用在竞争关系)

  • 如何将数据通过主线程传递到函数线程?通过全局空间。
  • 如何让主线程和函数线程同步?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <time.h>pthread_mutex_t mutex;//定义一个锁char buff[128]={0};//用来数据传递void *fun(void *arg)
{while(1){pthread_mutex_lock(&mutex);if(strncmp(buff,"end",3)==0){break;}printf("fun:%s\n",buff);memset(buff,0,128);int n=rand() %3+1;sleep(n);pthread_mutex_unlock(&mutex);n=rand() %3+1;sleep(n);}
}int main()
{stand((unsigned int)(time(NULL)*time(NULL)));pthread_mutex_init(&mutex,NULL);//初始化的锁是解锁状态pthread_t id;int res=pthread_create(&id,NULL,fun,NULL);assert(res==0);while(1){pthread_mutex_lock(&mutex);//加锁printf("input:");fgets(buff,127,stdin);pthread_mutex_unlock(&mutex);//解锁if(strncmp(buff,"end",3)==0) {break;}n=rand() %3+1;sleep(n);}  pthread_join(id,NULL);pthread_mutex_destroy(&mutex);exit(0);
}

二、信号量:特殊的一个计数器(进程的信号量可以在线程使用,但是线程的信号量不能在进程中使用)

1、信号量类型:全局,sem_t

2、初始化:

int  sem_init(sem_t  *sem, int shared, int val);

  • sem_t  *sem:sem_t类型的信号量的变量
  • shared:设定是否可以在进程之间共用,Linux目前不支持,一般为0,代表进程间不共享
  • val:初始值

3、P操作:

int sem_wait(sem_t *sem);

  • 如果信号量的值为0,P操作会阻塞

4、V操作

int sem_post(sem_t *sem);

5、销毁:

int  sem_destroy(sem_t *sem);

6、例题:主线程负责接收用户输入,函数线程负责将用户输入打印到终端界面(最好用信号量实现,因为互斥锁更多的用在竞争关系)

函数线程对主线程信号量为1,主线程对函数线程为0

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <semaphore.h>sem_t sem1;
sem_t sem2;//定义两个信号量char buff[128]={0};void *fun(void *arg)
{while(1){sem_wait(&sem1);//p操作if(strncmp(buff,"end",3)==0){break;}printf("fun: %s",buff)memset(buff,0,128);sem_pos(&sem2);//函数线程唤醒主线程}
}int main()
{sem_init(&sem1,0,0);sem_init(&sem2,0,1);int res=pthread_create(&id,NULL,fun,NULL);assert(res==0);while(1){sem_wait(&sem2);printf("input:");fgets(buff,127,stdin);sem_pos(&sem1);//主线程唤醒函数线程if(strncmp(buff,"end",3)==0){break;}}pthread_join(id,NULL);sem_destroy(&sem1);sem_destroy(&sem2);return 0;
}

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

1、初始化

int pthread_cond_init(pthread_cond_t*cond, pthread_condattr_t * attr);

  • attr表条件变量属性,通常为默认值,传NULL即可

  • 变量cond只有两种取值1、0

2、阻塞等待条件变量发生

int pthread_cond_wait(pthread_cond_tcond,pthread_mutex_tmutex);

用法:

1、 pthread_mutex_lock(&mutex);

2、 pthread_cond_wait(&cond,&mutex);
(1)mutex加锁
(2)wait调用,将线程放到cond现成列表中,然后解锁
(3)wait返回时,mutex又会被锁住(条件发生的时候返回)
(4)对mutex解锁

3、pthread_mutex_unlock(&mutex);

3、唤醒单个线程:

int pthread_cond_signal(pthread_cond_t*cond);////前面需要进行一次lock,后面进行一次unlock

4、唤醒所有等待的线程:

int pthread_cond_broadcast(pthread_cond_t * cond);//前面需要进行一次lock,后面进行一次unlock

5、销毁

int pthread_cond_destroy(pthread_cont_t * cond);

6、代码实现

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>#include <pthread.h>
char buff[128] = {0};pthread_cond_t  cond; // 条件变量
pthread_mutex_t  mutex;  // 互斥锁void *fun(void *arg)
{char *s = (char*)arg;while(1){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex); // wait是要阻塞的,直到有线程对cond进行signal或者broadcastpthread_mutex_unlock(&mutex);if(strncmp(buff, "end", 3) == 0){break;}printf("%s: %s", s, buff);}
}int main()
{pthread_mutex_init(&mutex, NULL);pthread_cond_init(&cond, NULL);pthread_t id[2];int res = pthread_create(&id[0], NULL, fun, "thread1");assert(res == 0);res = pthread_create(&id[1], NULL, fun, "thread2");assert(res == 0);while(1){printf("input: ");fgets(buff, 127, stdin);if(strncmp(buff, "end", 3) == 0) //  唤醒在条件变量cond上等待的所有线程{pthread_mutex_lock(&mutex);pthread_cond_broadcast(&cond);pthread_mutex_unlock(&mutex);break;}else // 唤醒在条件变量cond上等待的一个线程{pthread_mutex_lock(&mutex);pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);}}pthread_join(id[0], NULL);pthread_join(id[1], NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);exit(0);
}

四、读写锁 

1、概念:读写锁与互斥量类似,不过读写锁允许更高的并行性。读写锁可以有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。

2、初始化:

int pthread_relock_init(pthread_t* rlock,pthread_rwlockattr * attr);

3、读锁:

int pthread_rwlock_rdlock(pthread_rwlock_t *relock);

允许多个线程同时进行读加锁,所有线程都是执行读操作,不会改变数据

4、写锁:

int pthread_rwlock_wdlock(pthread_rwlock_t *relock);
int pthread_rwlock_udlock(pthread_rwlock_t *relock);

5、销毁:

int pthread_rwlock_destroypthread_rwlock_t *relock);

《UNIX环境高级编程》