同步 啥事同步,同时起步,协调一致的,然而不同的对象,又有不同的同步概念
设备同步,两个设备之间的一个共同时间的参考,数据库同步等等
在linxu中我们需要了解线程同步
线程同步 同步即协同一致,按照预订的先后次序运行
一个线程调用一个函数时,为了保证其他线程调用时的数据一致性,不能调用其他函数
这个时候我们就需要给这个调用的数据,上一个锁,对共享的区域做一个保护
所以在linux中当多个流程操作一个共享资源时都需要加锁
常用锁和常用原语 互斥量(互斥锁) pthread_mutex_init 初始化一个互斥锁
1 int pthread_mutex_init (pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr) ;
注意这个关键词restrict,指名只能使用该指针,不能通过其他指针操作内存指向
pthread_mutex_t *restrict mutex 可以单纯理解为锁 ,锁上是为0,解锁为1
mutex,const pthread_mutexattr_t *restrict attr 锁的属性
默认初始值为1
当不需要使用特殊配置时,可以直接使用宏变量赋值
1 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
来代替该函数
pthread_mutex_destory 删除一个互斥锁
1 int pthread_mutex_destroy (pthread_mutex_t *mutex) ;
一般在结束时,不再使用该锁时,进行调用删除
pthread_mutex_lock 顾名思义,上锁
1 int pthread_mutex_lock (pthread_mutex_t *mutex) ;
可以简单理解为对锁进行减1操作
pthread_mutex_unlock 顾名思义,释放锁
1 int pthread_mutex_unlock (pthread_mutex_t *mutex) ;
可以简单理解为对锁进行++操作
配合lock使用,
在不同的线程中需要访问同一个共享内存,都需要执行上锁和解锁的操作
使用代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <pthread.h> pthread_mutex_t mutex;void *printHello () { srand(time(NULL )); while (1 ) { pthread_mutex_lock(&mutex); printf ("hello " ); sleep(rand() % 3 ); printf ("world!\n" ); pthread_mutex_unlock(&mutex); sleep(rand() % 3 ); } return NULL ; } int main () { pthread_t tid; srand(time(NULL )); int count = 5 ; pthread_mutex_init(&mutex, NULL ); int ret = pthread_create(&tid, NULL , printHello, NULL ); if (ret != 0 ){ fprintf (stderr ,"error : %s" , strerror(ret)); exit (1 ); } while (count--) { pthread_mutex_lock(&mutex); printf ("HELLO " ); sleep(rand() % 3 ); printf ("WORLD!\n" ); pthread_mutex_unlock(&mutex); sleep(rand() % 3 ); } pthread_cancel(tid); pthread_join(tid, NULL ); pthread_mutex_destroy(&mutex); return 0 ; }
注意:锁的粒度应该是越小越好,访问共享操作后立即解锁
死锁 是一种现象,而不是一种新的锁机制。
产生的原因
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 pthread_mutex_t mutex1;pthread_mutex_t mutex2;int a = 0 ;int b = 0 ;void *tfn () { srand(time(NULL )); int count = 3 ; while (count--){ pthread_mutex_lock(&mutex2); sleep(rand() % 3 ); b += 100 ; pthread_mutex_lock(&mutex1); a += 1 ; pthread_mutex_unlock(&mutex2); pthread_mutex_unlock(&mutex1); printf ("i m thread, a = %d , b = %d \n" , a, b); } pthread_exit(NULL ); } int main () { int count = 5 ; pthread_t tid; srand(time(NULL )); pthread_mutex_init(&mutex1, NULL ); pthread_mutex_init(&mutex2, NULL ); int ret = pthread_create(&tid, NULL , tfn, NULL ); if (ret != 0 ){ fprintf (stderr , "error: %s\n" , strerror(ret)); exit (1 ); } while (count--){ pthread_mutex_lock(&mutex1); sleep(rand() % 3 ); a += 1 ; pthread_mutex_lock(&mutex2); b += 100 ; pthread_mutex_unlock(&mutex1); pthread_mutex_unlock(&mutex2); printf ("i m main, a = %d , b = %d \n" , a, b); } pthread_mutex_destroy(&mutex1); pthread_mutex_destroy(&mutex2); pthread_exit(NULL ); }
死锁的处理方式
第一种死锁
第二种
调用trylock,线程一方拿不到锁时,主动释放锁
读写锁 写独占,读共享。写锁的优先级更高
对一个共享数据进行操作的锁,常用在读次数大于写的情况。
当线程1读操作时,其他线程也可以对其进行读操作,这是共享模式锁
当线程执行写操作上了写操作时,其他线程都无法对其进行任何操作,这是独占模式锁
pthread_rwlock_init 初始化读写锁
1 int pthread_rwlock_init (pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr) ;
pthread_rwlock_destroy 删除锁
1 int pthread_rwlock_destroy (pthread_rwlock_t *rwlock) ;
pthread_rwlock_rdlock 设定一个读锁
pthread_rwlock_wdlock 设定一个写锁
pthread_rwlock_trywrlock 尝试写锁,非阻塞的写法
pthread_rwlock_unlock 解锁
其实读写锁的核心就14个字:
读时占用,写时共享,写锁优先级高
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <pthread.h> pthread_rwlock_t rwlock;int a = 0 ;void *th_write (void *arg) { int i = (int )arg; int olda; while (1 ) { olda = a; usleep(1000 ); pthread_rwlock_wrlock(&rwlock); printf ("====write :%d olda = %d, a = %d \n" , i, olda, ++a); pthread_rwlock_unlock(&rwlock); usleep(5000 ); } } void *th_read (void *arg) { int i = (int )arg; while (1 ) { pthread_rwlock_rdlock(&rwlock); printf ("read :%d a = %d\n" , i, a); pthread_rwlock_unlock(&rwlock); usleep(900 ); } } int main (int argc, char *argv[]) { if (argc != 3 ){ printf ("./name rtnums wtnums\n" ); exit (1 ); } int count_r = atoi(argv[1 ]); int count_w = atoi(argv[2 ]); int i; pthread_t tidr[count_r]; pthread_t tidw[count_w]; pthread_rwlock_init(&rwlock, NULL ); for (i = 0 ; i < count_w; i++){ int ret = pthread_create(&tidw[i], NULL , th_write, (void *)i); if (ret != 0 ){ fprintf (stderr , "error:%s\n" , strerror(ret)); exit (1 ); } } for (i = 0 ; i < count_r; i++) { int ret = pthread_create(&tidr[i], NULL , th_read, (void *)i); if (ret != 0 ){ fprintf (stderr , "error:%s\n" , strerror(ret)); exit (1 ); } } for (i = 0 ; i < count_r; i++) { pthread_join(tidr[i], NULL ); } for (i = 0 ; i < count_w; i++) { pthread_join(tidw[i], NULL ); } return 0 ; }
条件变量 条件变量本身不是锁,但是一样造成线程阻塞,说白了就是在互斥锁中进行条件判断,给多线程提供一个会合的场所
pthread_cond_wait 阻塞等待一个条件变量
1 int pthread_cond_wait (pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex) ;
第一个,老套路,一个结构体
第二个就有意思了,是一个互斥锁结构体。
一个看就知道这个函数不是等闲之辈
重点就是这个函数,有三个作用
阻塞等待条件变量cond满足
释放已掌握的互斥锁,实际上就是unlock(&mutex);
当被唤醒,pthread_cond_wait 函数返回时,解除阻塞并重新组阻塞并重新申请获取互斥锁相当于 lokc(&mutex);
注意这个唤醒,
有两种唤醒方式
pthread_cond_signal
pthread_cond_broadcast(广播)
pthread_cond_signal 唤醒满足条件变量的上的至少一个阻塞的线程
pthread_cond_broadcast 唤醒所有的满足的条件变量阻塞的线程
pthread_cond_timedwait 设定一个默认时间自动唤醒的wait
1 2 3 int pthread_cond_timedwait (pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime) ;
相较于一般的wait,多了一个结构体参数
const struct timespec abstime
绝对时间的意思
绝对时间实际上也是相对的,从1970年1月1号0000开始计时
time_t tv_sec 秒
long tv_nsec 纳秒
想要定时5秒钟实际上需要使用操作
1 2 3 time_t cur =time(NULL )struct timespec t;t.tv_sec = cur + 5 ;
生产者消费者模型实现 一个典型的线程同步的模型
生产者,产品,消费者
生产者进程负责生产产品
消费者进程负责消费产品
一个简单的实现代码
这里是定义了一个简单的链表节点,一个生产者每次生产一个,一个消费者也每次消费一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 struct msg { struct msg *next ; int num; }; struct msg *head ;struct msg *mp ;pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void *consumer (void *p) { for (;;){ pthread_mutex_lock(&lock); while (head == NULL ) { pthread_cond_wait(&has_product, &lock); } mp = head; head = mp->next; pthread_mutex_unlock(&lock); printf ("-Consume ----%d\n" , mp->num); free (mp); sleep(rand() % 5 ); } } void *producer (void *p) { for (;;){ mp = malloc (sizeof (struct msg)); mp->num = rand() %1000 + 1 ; printf ("-Produce ----%d\n" , mp->num); pthread_mutex_lock(&lock); mp->next = head; head = mp; pthread_mutex_unlock(&lock); pthread_cond_signal(&has_product); sleep(rand() % 5 ); } } int main () { pthread_t sid, cid; srand(time(NULL )); pthread_create(&sid, NULL , producer, NULL ); pthread_create(&cid, NULL , consumer, NULL ); pthread_join(sid, NULL ); pthread_join(cid, NULL ); return 0 ; }
条件变量的优点 相较于mutex,能减少不必要的竞争,如果没有释放,多个进程之间还会互相竞争
信号量 进化版的互斥锁,虽然也是线程库函数里的东西,但是,是可以用在进程间的锁
初始化的值是N值(与互斥锁默认值为1不同,N值是自己可以指定任意数值(必须是正整数),可以理解为锁的钥匙,资源量,*可以同时使用这把锁的进程数 。
出现的考量,是为了多个进程or线程需要共同访问一个的共享资源的同时,保证线程或者进程的并发性。
sem_init 1 int sem_init (sem_t *sem, int pshared, unsigned int value) ;
sem_t sem
pshared 是否在进程共享
unsigned int value 设定的值
sem_destroy 对应的删除函数
1 int sem_destroy (sem_t *sem) ;
sem_wait 加锁函数,等同于lock,一样可以理解为—操作,当值为0时,进行阻塞
1 int sem_wait (sem_t *sem) ;
sem_trywait 尝试加锁,同理trylock,非阻塞式加锁
1 int sem_trywait (sem_t *sem) ;
sem_post 解锁,等同于unlock,一样可以理解为++操作。
1 int sem_trywait (sem_t *sem) ;
sem_timedwait 定时加锁
1 int sem_timedwait (sem_t *sem, const struct timespec *abs_timeout) ;
同样是绝对时间
直接看代码示例吧
比如说实现一个线程每隔五秒打印“hello world”,用户与输入立马打印
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 sem_t lock;char str[64 ] = "" ;void *printHello (void *p) { for (;;){ time_t cur = time(NULL ); struct timespec t ; t.tv_sec = cur + 5 ; sem_timedwait(&lock, &t); printf ("Hellp world\n" ); } } void *scanInput (void *p) { for (;;){ while (strlen (str) == 0 ){ fgets(str, sizeof (str), stdin ); } sem_post(&lock); memset (str, 0 , 64 ); } } int main () { pthread_t pid, cid; sem_init(&lock, 0 , 1 ); pthread_create(&pid, NULL , printHello, NULL ); pthread_create(&cid, NULL , scanInput, NULL ); pthread_join(pid, NULL ); pthread_join(cid, NULL ); return 0 ; }
还是典型的消费者生产者模型 上方实现的模型的一对一的进化版
一个生产者,多个产品,一个消费者,不过此时消费者在某种意义上来说也是一种生产者的角色,生产一个空,此时公共区产品使用queue(队列)表示
一个生产者,多个产品,二个消费者,生产速率和消费速率是随机的,代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <semaphore.h> #define NUM 5 #define CNUM 2 int queue [NUM];sem_t blank_number, product_number;sem_t consumer_number;void *producer () { int i = 0 ; while (1 ){ sem_wait(&blank_number); queue [i] = i + 1 ; printf ("Produce----%d\n" , queue [i]); sem_post(&product_number); i = (i+1 ) % NUM; sleep(rand() % 1 ); } } void *consumer (void *arg) { int j = (int )arg; int i = 0 ; while (1 ){ sem_wait(&product_number); sem_wait(&consumer_number); while (queue [i] == 0 ){ i = (i+1 ) % NUM; } printf ("Consumer NO.%d----%d\n" , j, queue [i]); queue [i] = 0 ; sem_post(&consumer_number); sem_post(&blank_number); i = (i+1 ) % NUM; sleep(rand() % 3 ); } } int main () { pthread_t cid[CNUM], pid; sem_init(&blank_number, 0 , NUM); sem_init(&product_number, 0 , 0 ); sem_init(&consumer_number, 0 , CNUM); pthread_create(&pid, NULL , producer, NULL ); for (int i = 0 ; i < CNUM; i++){ pthread_create(&cid[i], NULL , consumer, (void *)i); } for (int i = 0 ; i < CNUM; i++){ pthread_join(cid[i], NULL ); } pthread_join(pid, NULL ); sem_destroy(&blank_number); sem_destroy(&product_number); return 0 ; }
注意这其实只是一对多的情况,一个生产者,多个产品,多个消费者,双方的速率也都是随机的,而且没有将“拿出”和“消耗”的产品的操作单独分开,比较简陋
单纯讨论线程间的同步问题,双方速率和产品 暂且不论,有两个关键变量,生产者个数,消费者个数,就有4种情况
一生产,一消费。
多生产,一消费。
一生产,多消费。
多生产,多消费。
思想是一样的,每有需要进行同步的线程组,就需要设定一个锁(不是绝对,根据需要
生产者与消费者之间,就需要一个锁,而消费者与消费者之间也会需要一个锁,确保每个消费者都能拿到数据
进程间的互斥量 如果想要将这些线程库中的锁给进程使用呢?
这个时候就需要在进行锁的定义时对锁的属性进行修改
进程的锁 定义一个锁属性的结构体
1 pthread_mutexattr_t mutexattr;
需要对这个属性在进行初始化
1 pthread_mutexattr_init(&mutexattr);
调用设置属性函数
1 pthread_mutexattr_setpshared(&mutexattr, int pshared);
注意第二个参数的取值
PTHREAD_PROCESS_SHARED(公开给进程使用的锁)
PTHREAD__PROCESS_PRIVATE(线程私有的锁);
当不需要使用该属性结构体时同样需要调用destroy函数对属性进行删除
1 pthread_mutexattr_destroy(&mutexattr);
文件锁 基于fcntl实现,说实话我都忘了咋怎么用了,隐约记得一般用来修改文件的属性。
锁的设置,区别不大其实,只不过换了个函数实现而已
1 int fcntl (int fd, int cmd, ... ) ;
需要用到的宏参,第二个参数
F_SETLK 设置文件锁(trylock)
F_SETLKW 设置文件锁 (lokc) W wait
F_GETLK 获取当前文件锁
要设置的锁的结构体,同时也是第三个参数
struct flock(这里列用到的数据)
…..
short l_type;
锁的类型
F_RDLCK(读锁)
F_WRLCK(写锁)
F_UNLCK(无文件锁)
short l_whence
文件的指针的起始位置
SEEK_SET
SEEK_END
SEEK_CUR
off_t l_start
off_t l_len
pid_t l_pid
使用代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 int main (int argc, char *argv[]) { if (argc != 2 ){ printf ("filelock r/w\n" ); exit (1 ); } int fd = open("文件锁文件.txt" , O_CREAT|O_RDWR, 0777 ); if (fd < 0 ){ perror("open" ); exit (1 ); } struct flock lock ; lock.l_whence = SEEK_SET; lock.l_start = 0 ; lock.l_len = 0 ; if (strcmp (argv[1 ], "r" ) == 0 ){ lock.l_type = F_RDLCK; }else { lock.l_type = F_WRLCK; } fcntl(fd, F_SETLKW, &lock); printf ("get flokc\n" ); sleep(10 ); printf ("unlock!\n" ); return 0 ; }
开了两个终端测试发现,文件锁的规则其实就是,写时独占,读时共享
注意这是,进程间独占的锁机制
在线程中实际上使用的读写锁
哲学家进餐 五个哲学家在一个餐桌上吃饭,每个人只有一根筷子,想要吃上中间的菜,一个人需要完整的一双筷子(
一个人想要吃菜,就拿下一个人的筷子(咦惹
1—-2
2—-3
3—-4
4—-5
5—-1
这个时候如果5个人同时想吃菜,五个人都拿了下一个人的筷子,自己的筷子同时也被前一个人拿走了,这个时候就出现一个一比较尴尬的状态。如果此时谁也不肯放那么就没有人能够吃菜。
代码模拟一下这个问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <string.h> #include <unistd.h> #define COUNT 5 pthread_mutex_t mutex[COUNT];void *diner (void *arg) { int i = (int )arg; while (1 ) { if (i == 4 ){ pthread_mutex_lock(&mutex[i]); pthread_mutex_lock(&mutex[0 ]); printf ("%d:好吃!\n" , i); pthread_mutex_unlock(&mutex[i]); pthread_mutex_unlock(&mutex[0 ]); }else { pthread_mutex_lock(&mutex[i]); pthread_mutex_lock(&mutex[i+1 ]); printf ("%d:好吃!\n" , i); pthread_mutex_unlock(&mutex[i]); pthread_mutex_unlock(&mutex[i+1 ]); } } pthread_exit(NULL ); } int main () { int i; pthread_t tid[COUNT]; for (i = 0 ; i < COUNT; i++) { pthread_mutex_init(&mutex[i], NULL ); } for ( i = 0 ; i < COUNT; i++) { int ret = pthread_create(&tid[i], NULL , diner, (void *)i); if (ret != 0 ){ fprintf (stderr , "error:%s" , strerror(ret)); exit (1 ); } } for ( i = 0 ; i < COUNT; i++) { pthread_join(tid[i], NULL ); } return 0 ; }
不得不说我运气有点好。运行了好5秒都没出现死锁现象,还以为写错了,好在第二次进行运行时死锁了。
死锁了咋整呢?
有三种解决方案
有个人主动放弃拿自己的筷子,先去拿别人的筷子,这样就能保证自己的筷子能被下一个人拿到凑成两个。
再定义一个互斥量,用来锁住其他人拿到筷子,保证有一个人拿到一双
嘶,想了想好像不知三种解决方案啊。比如什么不同的序号不同取筷子的顺序,或者定义一个为n-1的信号量,有一个人必须等待其他n个人执行拿筷子动作后都成功后才能拿筷子。等等。
归根结底,只要保证有一个人肯定能够拿到一双筷子就可以实现永动。
对了忘了这里再补个进程版的哲学家问题。
这里使用信号量作为锁
当然也可以用mutex
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 nt main () { int i; sem_t *lock= mmap(NULL , sizeof (sem_t )* count, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1 , 0 ); if (lock == MAP_FAILED){ perror("mmap" ); exit (1 ); } for ( i = 0 ; i < count; i++) { sem_init(&lock[i], 1 , 1 ); } pid_t pid; for (i = 0 ; i < count; i++) { pid = fork(); if (pid == 0 ){ break ; }else if (pid < 0 ){ perror("fork\n" ); exit (1 ); } } if (i < 5 ){ int left, right; if (i == 4 ){ left = i; right = 0 ; }else { left = i; right = i + 1 ; } while (1 ){ sem_wait(&lock[left]); sem_wait(&lock[right]); printf ("%d: 进餐\n" , i); sem_post(&lock[left]); sem_post(&lock[right]); } }else { pid_t wpid; do { wpid = waitpid(-1 , NULL , WNOHANG); if (wpid < 0 ){ perror("error" ); exit (1 ); } }while (wpid != -1 ); for ( i = 0 ; i < count; i++) { sem_destroy(&lock[i]); } munmap(lock,sizeof (sem_t )*count); return 0 ; } }
需要注意的事进程需要用到锁时
注意初始化的锁的属性,是否分享给进程使用。
解决方案代码 这里简单例一个比较简单的解决方式
再加一个锁,获取当有有人拿起筷子时,其他人都不能动
当然,这样原本好好的并行线程就给搞成串行了,能不能再优化呢?答案是可以
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 pthread_mutex_t mutex[COUNT];pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void *diner (void *arg) { int i = (int )arg; while (1 ) { if (i == 4 ){ pthread_mutex_lock(&lock); pthread_mutex_lock(&mutex[i]); pthread_mutex_lock(&mutex[0 ]); pthread_mutex_unlock(&lock); printf ("%d:好吃!\n" , i); pthread_mutex_unlock(&mutex[i]); pthread_mutex_unlock(&mutex[0 ]); }else { pthread_mutex_lock(&lock); pthread_mutex_lock(&mutex[i]); pthread_mutex_lock(&mutex[i+1 ]); pthread_mutex_unlock(&lock); printf ("%d:好吃!\n" , i); pthread_mutex_unlock(&mutex[i]); pthread_mutex_unlock(&mutex[i+1 ]); } }
只需要,把加的读写锁改成信号量,最多可以实现4个线程,不过有一个线程就得先当冤大头了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 pthread_mutex_t mutex[COUNT];sem_t lock;void *diner (void *arg) { int i = (int )arg; while (1 ) { if (i == 4 ){ sem_wait(&lock); pthread_mutex_lock(&mutex[i]); pthread_mutex_lock(&mutex[0 ]); printf ("%d:好吃!\n" , i); pthread_mutex_unlock(&mutex[i]); sem_post(&lock); pthread_mutex_unlock(&mutex[0 ]); }else { sem_wait(&lock); pthread_mutex_lock(&mutex[i]); pthread_mutex_lock(&mutex[i+1 ]); printf ("%d:好吃!\n" , i); pthread_mutex_unlock(&mutex[i]); sem_post(&lock); pthread_mutex_unlock(&mutex[i+1 ]); } }
当然也可以,将奇数线程,先拿左手筷子,偶数线程先拿右手筷子,交错拿。也可以避免死锁问题
等等,思想是不变的,保证只要总有边界资源能够释放就是可以避免