多线程与互斥锁

Posted 西工大舞蹈机器人基地

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程与互斥锁相关的知识,希望对你有一定的参考价值。

例如以下代码段,当 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 互斥锁的保护范围和使用顺序

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


典型的情况是,两个线程执行时都需要锁定互斥锁 A 和 B,在一个线程中,锁定顺序是先锁定 A,后锁定 B,而另一个线程的锁定顺序是先锁定 B,再锁定 A。这种情况下,当一个线程已经锁定了 A 而另一个线程恰好锁定了 B 时,双方因互相争用对方已锁定的互斥锁,谁也不让步,而陷入死锁状态。

另一种情况是,一个线程已经锁定了互斥锁 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);


前者可以在锁定失败后立即返回,后者可以在一段超时时间后返回,应用这两个函数可以处理这种错误情况,而避免陷入无限的死锁中。



以上是关于多线程与互斥锁的主要内容,如果未能解决你的问题,请参考以下文章

并发编程之多线程

ThreadX内核源码分析 - 线程同步之互斥锁及动态优先级

整数的无锁多线程

Python网络编程(进程通信信号线程锁多线程)

无锁多线程编程初步(基础部分)

LINXU多线程(进程与线程区别,互斥同步)