zoukankan      html  css  js  c++  java
  • 【Linux 编程】线程同步

      有关线程创建:【Linux 编程】线程编程

      当多个控制线程共享相同的数据时,需要确保每一个线程看到一致的数据。当一个线程修改数据变量时,其他线程在读取这个变量值时就可能会看到不一致的数据。在变量修改时间多于一个存储访问的处理结构中,当存储器读取或存储器写这两个周期交叉时,潜在的数据不一致性就会出现。为了解决这种数据不一致性的问题,线程利用锁机制来保证在保证数据的一致性问题。若读取数据或写数据操作是原子操作,则不存在数据竞争或数据不一致性问题。

      1. 互斥锁(pthread_mutex_t)

      互斥锁保证同一时间只允许一个线程访问数据。任何其他试图获取已加锁的互斥锁,就会被阻塞直到当前线程释放该互斥锁。若有多个线程被阻塞,在互斥锁被释放后,将唤醒所有阻塞线程,并采取先到先服务的策略来分配互斥锁的使用权。

      互斥锁的初始化

    • 使用常量PTHREAD_MUTEX_INITIALIZER来初始化静态分配的互斥量;
    • 针对动态分配的互斥量,则调用函数pthread_mutex_init()进行初始化;调用函数pthread_mutex_destroy()来去初始化。需要注意,这两个函数成对出现,使用方式类似于malloc/free。

      互斥锁的使用

    • 函数pthread_mutex_lock()用来对互斥锁加锁。若互斥锁已上锁,则当前线程将被阻塞;
    • 若系统线程在不能获取互斥锁时被阻塞,则调用函数pthread_mutex_trylock()尝试对互斥锁进行加锁。若成功获取互斥锁,则锁住互斥锁并返回0;若失败,则返回非0,并设置错误码为EBUSY。
    • 函数pthread_mutex_unlock()用来对互斥锁进行解锁。

      若线程试图对同一个互斥锁加锁两次,那么它自身将会陷入死锁状态。在使用锁机制时,需要预先设计加锁的顺序来避免死锁的发生。

      例子:  

     1 #include <unistd.h>
     2 #include <pthread.h>
     3 #include <stdio.h>
     4 
     5 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
     6 
     7 void *start_routine(void *arg)
     8 {
     9     int res;
    10     int times = 0;
    11     pthread_t thread_id = pthread_self();
    12 
    13     while (1) {
    14         res = pthread_mutex_trylock(&lock);
    15         if (res != 0)
    16         {
    17             ++times;
    18             printf("Now the mutex lock is using!");
    19             printf("And thread ID 0x%x is trying %d times!\n",
    20                 (unsigned int) thread_id, times);
    21             sleep(2);
    22             continue;
    23         }
    24 
    25         printf("OK, 0x%x get mutex\n",     (unsigned int) thread_id);
    26         times = 0;
    27         sleep(10);
    28         pthread_mutex_unlock(&lock);
    29         
    30         if (times == 0)
    31             break;
    32     }
    33 
    34     pthread_exit((void *)thread_id);
    35 }
    36 
    37 int main()
    38 {
    39     pthread_t id0, id1;
    40     int res;
    41 
    42     res = pthread_create(&id0, NULL, start_routine, NULL);
    43     if (res < 0)
    44     {
    45         perror("pthread_create error!");
    46         return (-1);
    47     }
    48     
    49     res = pthread_create(&id1, NULL, start_routine, NULL);
    50     if (res < 0)
    51     {
    52         perror("thread_create error!");
    53         return (-1);
    54     }
    55 
    56     sleep(20);
    57     printf("MutexDemo is over!\n");
    58 
    59     return 0;
    60 
    61 }
    View Code

      运行结果

      

      

      在多线程设计过程中不仅需要考虑加锁的顺序,还需要考虑锁的粒度大小问题,即加锁中处理数据的过程。若粒度太粗,就会发生出现很多线程阻塞等待相同的锁,则导致并发性的性能改善微乎其微。如果锁的粒度太小,那么过多的锁开销会使系统性能受到影响,而且代码会变得更加复杂。在设计加锁时,需要在满足锁需求的情况下,在代码复杂性和优化性能找到好的平衡点。

      2. 读写锁(pthread_rwlock_t)

      不同于互斥锁一次只能有一个线程可以对其加锁。读写锁具有三种状态:读模式下加锁状态、写模式下加锁状态、不加锁状态。一次只有一个线程可以占有写模式的读写锁,但多个线程可以同时占有读模式的读写锁。因此,读写锁也可以叫做共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的

      若读写锁以读模式锁住状态时,如果有另外的线程试图从写模式加锁,将阻塞随后的读模式锁请求。若多个线程共同以写模式加锁,处理方式与互斥锁处理方式相同。

      读写锁的初始化使用pthread_rwlock_init()来完成,在释放它们底层内存之前需要调用函数pthead_rwlock_destroy()来去初始化。

      读写锁的使用

    • 读模式加锁,pthread_rwlock_rdlock()和pthread_rwlock_tryrdlock();
    • 写模式加锁,pthread_rwlock_wrlock()和pthread_rwlock_trywrlock();
    • 解锁操作,pthread_rwlock_unlock()。

      3.条件变量(pthread_cond_t)

      条件变量与互斥锁一起使用的,允许线程以无竞争的方式等待特定的条件发生。线程在改变条件状态之前首先锁住互斥锁,其他线程在获取互斥锁之前不会察觉到这种改变,因此必须锁定互斥锁以后才能计算条件。

      条件变量的初始化

    • 用常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量;
    • 若条件变量是动态分配的,则利用pthread_cond_init()函数进行初始化;在释放底层内存空间之前,必须使用pthread_cond_destroy()函数对条件变量去除初始化。

      条件变量使用函数pthread_cond_wait()等待条件为真。函数中的互斥锁对条件进行保护,调用者把锁住的互斥锁传给函数。函数把调用线程放在等待条件的线程列表中,然后对互斥锁解锁,这两个操作是原子操作。函数在返回时,将再次获得互斥锁的控制权。函数pthread_cond_timedwait()在满足pthread_cond_wait()函数的处理方式的基础上,加上超时处理。当在规定时间内条件不能满足,则生成一个错误码并返回。

      有两个函数用于通知线程条件已满足。pthread_cond_signal()函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast()函数将唤醒等待该条件的所有线程。

      有关条件变量的编程例子:【Linux 编程】pthead_cond_t 的使用

  • 相关阅读:
    LeetCode Best Time to Buy and Sell Stock
    LeetCode Scramble String
    LeetCode Search in Rotated Sorted Array II
    LeetCode Gas Station
    LeetCode Insertion Sort List
    LeetCode Maximal Rectangle
    Oracle procedure
    浏览器下载代码
    Shell check IP
    KVM- 存储池配置
  • 原文地址:https://www.cnblogs.com/life91/p/3118485.html
Copyright © 2011-2022 走看看