翼度科技»论坛 云主机 LINUX 查看内容

Linux线程同步必知,常用方法揭秘!

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15
一、为什么要线程同步

在Linux 多线程编程中,线程同步是一个非常重要的问题。如果线程之间没有正确地同步,就会导致程序出现一些意外的问题,例如:

  • 竞态条件(Race Condition):多个线程同时修改同一个共享变量,可能会导致不可预测的结果,因为线程的执行顺序是不确定的。
  • 死锁(Deadlock):当两个或多个线程互相等待对方释放资源时,可能会导致死锁,这会导致程序无法继续执行。
  • 活锁(Livelock):当多个线程相互响应对方的动作,而没有任何进展时,可能会导致活锁,这也会导致程序无法继续执行。


  • 两个人在走路时需要相互让路,两个人都想让对方先通过,但最终还是没有人通过,这就是一种活锁情况

接下来将介绍互斥锁、条件变量、信号量、读写锁这几种线程同步方法,并使用C语言代码示例说明其使用方法。

二、互斥锁

互斥锁是一种用于线程同步的锁,用于保护共享资源。只有拥有该锁的线程才能访问共享资源,其他线程需要等待锁被释放后才能继续执行。
在Linux环境下,我们可以使用pthread库提供的互斥锁函数来实现互斥锁机制。以下是一些常用的互斥锁函数:
函数名描述pthread_mutex_init初始化互斥锁pthread_mutex_lock加锁互斥锁pthread_mutex_trylock尝试加锁互斥锁pthread_mutex_unlock解锁互斥锁pthread_mutex_destroy销毁互斥锁初始化互斥锁

在使用互斥锁之前,需要先初始化互斥锁。pthread_mutex_init函数用于初始化一个互斥锁。函数原型如下:
  1. int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
复制代码
其中,mutex参数是一个指向pthread_mutex_t结构体的指针,用于指定要初始化的互斥锁;attr参数是一个指向pthread_mutexattr_t结构体的指针,用于指定互斥锁的属性,通常设置为NULL。
以下是一个初始化互斥锁的例子:
  1. #include <pthread.h>
  2. pthread_mutex_t mutex;
  3. int main()
  4. {
  5.     // 初始化互斥锁
  6.     pthread_mutex_init(&mutex, NULL);
  7.    
  8.     // ...
  9.    
  10.     // 销毁互斥锁
  11.     pthread_mutex_destroy(&mutex);
  12.    
  13.     return 0;
  14. }
复制代码
加锁互斥锁

加锁互斥锁用于保证同一时刻只有一个线程能够访问共享资源。pthread_mutex_lock函数用于加锁一个互斥锁。函数原型如下:
  1. int pthread_mutex_lock(pthread_mutex_t *mutex);
复制代码
其中,mutex参数是一个指向pthread_mutex_t结构体的指针,用于指定要加锁的互斥锁。
以下是一个加锁互斥锁的例子:
  1. #include <pthread.h>
  2. pthread_mutex_t mutex;
  3. void* thread_func(void* arg)
  4. {
  5.     // 加锁互斥锁
  6.     pthread_mutex_lock(&mutex);
  7.    
  8.     // 访问共享资源
  9.     // ...
  10.    
  11.     // 解锁互斥锁
  12.     pthread_mutex_unlock(&mutex);
  13.    
  14.     return NULL;
  15. }
  16. int main()
  17. {
  18.     // 初始化互斥锁
  19.     pthread_mutex_init(&mutex, NULL);
  20.    
  21.     // 创建线程
  22.     pthread_t tid;
  23.     pthread_create(&tid, NULL, thread_func, NULL);
  24.    
  25.     // ...
  26.    
  27.     // 等待线程结束
  28.     pthread_join(tid, NULL);
  29.    
  30.     // 销毁互斥锁
  31.     pthread_mutex_destroy(&mutex);
  32.    
  33.     return 0;
  34. }
复制代码
尝试加锁互斥锁

尝试加锁互斥锁与加锁互斥锁的主要区别在于,如果互斥锁已经被其他线程锁定了,尝试加锁互斥锁将不会阻塞当前线程,而是会立即返回一个错误代码。函数原型如下:
  1. int pthread_mutex_trylock(pthread_mutex_t *mutex);
复制代码
其中,mutex参数是一个指向pthread_mutex_t结构体的指针,用于指定要尝试加锁的互斥锁。
以下是一个尝试加锁互斥锁的例子:
  1. #include <pthread.h>
  2. pthread_mutex_t mutex;
  3. void* thread_func(void* arg)
  4. {
  5.     // 尝试加锁互斥锁
  6.     int ret = pthread_mutex_trylock(&mutex);
  7.     if (ret == 0) {
  8.         // 访问共享资源
  9.         // ...
  10.         
  11.         // 解锁互斥锁
  12.         pthread_mutex_unlock(&mutex);
  13.     } else {
  14.         // 互斥锁已经被其他线程锁定了
  15.         // ...
  16.     }
  17.    
  18.     return NULL;
  19. }
  20. int main()
  21. {
  22.     // 初始化互斥锁
  23.     pthread_mutex_init(&mutex, NULL);
  24.    
  25.     // 创建线程
  26.     pthread_t tid;
  27.     pthread_create(&tid, NULL, thread_func, NULL);
  28.    
  29.     // ...
  30.    
  31.     // 等待线程结束
  32.     pthread_join(tid, NULL);
  33.    
  34.     // 销毁互斥锁
  35.     pthread_mutex_destroy(&mutex);
  36.    
  37.     return 0;
  38. }
复制代码
解锁互斥锁

解锁互斥锁用于释放已经锁定的互斥锁。pthread_mutex_unlock函数用于解锁一个互斥锁。函数原型如下:
  1. int pthread_mutex_unlock(pthread_mutex_t *mutex);
复制代码
其中,mutex参数是一个指向pthread_mutex_t结构体的指针,用于指定要解锁的互斥锁。
以下是一个解锁互斥锁的例子:
  1. #include <pthread.h>
  2. pthread_mutex_t mutex;
  3. void* thread_func(void* arg)
  4. {
  5.     // 加锁互斥锁
  6.     pthread_mutex_lock(&mutex);
  7.    
  8.     // 访问共享资源
  9.     //
  10.         // 解锁互斥锁
  11.         pthread_mutex_unlock(&mutex);
  12.         return NULL;
  13. }
复制代码
销毁互斥锁

在不再需要使用互斥锁时,需要将互斥锁销毁。pthread_mutex_destroy函数用于销毁一个互斥锁。函数原型如下:
  1. int pthread_mutex_destroy(pthread_mutex_t *mutex);
复制代码
其中,mutex参数是一个指向pthread_mutex_t结构体的指针,用于指定要销毁的互斥锁。
以下是一个销毁互斥锁的例子:
  1. #include <pthread.h>
  2. pthread_mutex_t mutex;
  3. int main()
  4. {
  5.     // 初始化互斥锁
  6.     pthread_mutex_init(&mutex, NULL);
  7.    
  8.     // ...
  9.    
  10.     // 销毁互斥锁
  11.     pthread_mutex_destroy(&mutex);
  12.    
  13.     return 0;
  14. }
复制代码
示例程序

下面是一个简单的示例程序,演示了如何使用互斥锁来同步两个线程的访问。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <pthread.h>
  4. pthread_mutex_t mutex;
  5. int shared_data = 0;
  6. void *thread_func(void *arg)
  7. {
  8.     int i;
  9.     for (i = 0; i < 1000000; i++) {
  10.         pthread_mutex_lock(&mutex);
  11.         shared_data++;
  12.         pthread_mutex_unlock(&mutex);
  13.     }
  14.     return NULL;
  15. }
  16. int main()
  17. {
  18.     pthread_t thread1, thread2;
  19.     pthread_mutex_init(&mutex, NULL);
  20.     pthread_create(&thread1, NULL, thread_func, NULL);
  21.     pthread_create(&thread2, NULL, thread_func, NULL);
  22.     pthread_join(thread1, NULL);
  23.     pthread_join(thread2, NULL);
  24.     pthread_mutex_destroy(&mutex);
  25.     printf("Shared data: %d\n", shared_data);
  26.     return 0;
  27. }
复制代码
在这个程序中,thread_func函数是两个线程执行的函数,它会对shared_data变量进行1000000次加一操作。
为了确保多个线程不会同时访问shared_data变量,我们使用了一个互斥锁。当一个线程要访问shared_data变量时,它会调用pthread_mutex_lock函数来加锁。如果锁已经被其他线程持有,那么这个线程就会被阻塞,直到锁被释放为止。当线程完成对shared_data变量的操作后,它会调用pthread_mutex_unlock函数来释放锁。
在这个程序执行完毕后,我们可以通过打印shared_data变量的值来检查程序是否正确地同步了两个线程的访问。如果程序正确地同步了线程的访问,那么shared_data变量的值应该是2000000。


来源:https://www.cnblogs.com/Wayne123/p/17278046.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具