ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

线程同步与互斥锁

2019-05-31 18:51:49  阅读:225  来源: 互联网

标签:同步 int 互斥 临界 线程 pthread mutex


相比多进程模型,多线程模型最大的优势在于数据共享非常方便,同一进程内的多个线程可以使用相同的地址值访问同一块内存数据。但是,当多个线程对同一块内存数据执行“读−处理−更新”操作时,会由于线程的交叉执行而造成数据的错误。

例如以下代码段,当 thread_func() 同时在多个线程中执行时,更新到 glob_value 中的值就会互相干扰,产生错误结果。

#define LOOP_COUNT   1000000
int glob_value = 0;

void * thread_func(void * args)
{
    int counter = 0;
    while(counter++ < LOOP_COUNT)
    {
        int local = glob_value;
        local++;
        glob_value = local;
    }
}

解决这类问题的关键在于,当一个线程正在执行“读−处理−更新”操作时,保证其他线程不会中途闯入与其交叉执行。不可被打断的执行序列称为临界区,保证多个线程不会交叉执行同一临界区的技术称为线程同步。

1 互斥锁的使用

最常用的线程同步技术是互斥锁,Linux 线程库中的相关函数有:

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

这里pthread的p代表POSIX线程

所有线程都有一个线程号,也就是Thread ID。其类型为pthread_t。通过调用pthread_self()函数可以获得自身的线程号。

pthread_mutex_lock() 负责在进入临界区之前对临界区加锁;
pthread_mutex_unlock() 负责在执行完临界区处理时给临界区解锁。

当某个线程试图给一个已经处在加锁状态的临界区再次加锁时,该线程就会被临时挂起,一直等到该临界区被解锁后,才会被唤醒并继续执行。

如果同时有多个线程等待某个临界区解锁,那下次被唤醒的进程取决于内核的调度策略,并没有固定的顺序。

静态分配的 mutex 变量在使用之前应该被初始化为 PTHREAD_MUTEX_INITIALIZER,而动态分配的 mutex 需要调用 pthread_mutex_init() 进行初始化,且只被某个线程初始化一次,可以利用 pthread_once() 函数方便完成。

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));

多个线程在临界区上的执行是串行的,开发者应该尽量减少程序在临界区内的停留时间,以提高程序的并行性。因此,临界区不应该包含任何非必须的逻辑,以及任何可能带来高延迟的 IO 等操作

2 互斥锁的保护范围和使用顺序

对互斥锁加锁的不恰当使用会造成线程的死锁,比如下面这两种情况。

  1. 典型的情况是,两个线程执行时都需要锁定互斥锁 A 和 B,在一个线程中,锁定顺序是先锁定 A,后锁定 B,而另一个线程的锁定顺序是先锁定 B,再锁定 A。这种情况下,当一个线程已经锁定了 A 而另一个线程恰好锁定了 B 时,双方因互相争用对方已锁定的互斥锁,谁也不让步,而陷入死锁状态。
  1. 另一种情况是,一个线程已经锁定了互斥锁 A,但在其后的处理逻辑中试图再次锁定 A,这时该线程会让自己陷入睡眠状态,再也等不到被唤醒的时候。

因此,开发者需要仔细规划互斥锁保护范围和使用顺序

3 避免死锁的两个加锁函数

为了避免出现死锁问题,可以使用另外两种变体的锁定函数,如下所示:

int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);

前者可以在锁定失败后立即返回,后者可以在一段超时时间后返回,

应用这两个函数可以处理这种错误情况,而避免陷入无限的死锁中。

在 Linux 中,实现互斥锁采用的是 Futex(Fast Userspace Mutex)方案。在该实现中,只有发生了锁的争用才需要陷入到内核空间中处理,否则所有的操作都可以在用户空间内快速完成。在大多数情况下,互斥锁本身的效率很高,其平均开销大约相当于几十次内存读写和算数运算所花费的时间。

标签:同步,int,互斥,临界,线程,pthread,mutex
来源: https://blog.csdn.net/weixin_35695879/article/details/90721330

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有