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 的使用

  • 相关阅读:
    javascript的window.open()具体解释
    Async.js解决Node.js操作MySQL的回调大坑
    入门--JTBC系统学习(1)
    Hadoop1.2.1 全然分布式集群搭建实操笔记
    Hessian原理与程序设计
    pycharm最新注册方法 pycharm最新激活方法 2016pycharm最新注册方法
    如何修改linux时间? 校正linux系统的时间
    python 内建函数 type() 和 isinstance() 介绍
    标准类型内建函数 str()和 repr() (及 `` 运算符) 简单介绍
    标准类型内建函数 cmp()介绍
  • 原文地址:https://www.cnblogs.com/life91/p/3118485.html
Copyright © 2011-2022 走看看