多线程编程--互斥锁、条件变量

注:本文用于个人学习/复习,代码参考 互斥锁_哔哩哔哩_bilibili

1、线程同步

多个线程访问同一块共享资源,需要按照顺序依次执行访问,线程对内存的这种访问方式被称之为线程同步

实现线程同步有以下的几种方法:

互斥锁

条件变量

信号量

自旋锁

读写锁

2、互斥锁

1)初始化锁

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutex_attr);

mutex_attr参数选择:
* PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
* PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
* PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样保证当不允许多次加锁时不出现最简单情况下的死锁。
* PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

或者定义互斥锁的时候直接使用下面语句对锁进行初始化:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

2)阻塞加锁,若互斥锁已经被其他线程上锁,则调用者一直阻塞等待,直到被解锁后才上锁

int pthread_mutex_lock(pthread_mutex_t *mutex);

3)非阻塞加锁,若互斥锁未加锁,则上锁,若互斥锁已加锁,则函数立即返回失败

int pthread_mutex_trylock(pthread_mutex_t *mutex);

4)解锁

int pthread_mutex_unlock(pthread_mutex_t *mutex);

5)销毁锁,释放资源

int pthread_mutex_destroy(pthread_mutex_t *mutex);

互斥锁存在优先唤醒的过程

测试demo如下:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

using std::cout;
using std::endl;

char buffer[100];

pthread_mutex_t mutex;

void *callback(void *arg)
{
        for(int i = 0; i < 3; i++) {
                // cout << time(0) << ":  lock --- pthread " << (long)arg << endl;
                pthread_mutex_lock(&mutex);
                cout << time(0) << ":  lock --- pthread " << (long)arg << endl;

                sprintf(buffer, "%d:%ld,%d", time(0), pthread_self(), i);
                sleep(5);

                pthread_mutex_unlock(&mutex);
                cout << time(0) << ":  unlock ---pthread " << (long)arg << endl;
                
                usleep(100);
        }
}

int main()
{
        pthread_mutex_init(&mutex, NULL);

        pthread_t tid1, tid2;
        pthread_create(&tid1, NULL, callback, (void *)1);
        pthread_create(&tid2, NULL, callback, (void *)2);

        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL);

        pthread_mutex_destroy(&mutex);

        return 0;
}

执行结果如下:

1676601133:  lock --- pthread 1
1676601138:  unlock ---pthread 1
1676601138:  lock --- pthread 2
1676601143:  unlock ---pthread 2
1676601143:  lock --- pthread 1
1676601148:  unlock ---pthread 1
1676601148:  lock --- pthread 2
1676601153:  unlock ---pthread 2
1676601153:  lock --- pthread 1
1676601158:  unlock ---pthread 1
1676601158:  lock --- pthread 2
1676601163:  unlock ---pthread 2

从测试log可以看出,各个线程逐个逐个访问全局变量buffer

3、条件变量

条件变量用来阻塞一个线程,直到其他的线程通知它条件已满足为止

条件变量看似简单,与互斥锁同时使用时非常巧妙

1)初始化条件变量

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);


或者定义条件变量的时候直接使用下面语句对条件变量进行初始化:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

2)阻塞等待

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

3)超时等待

int pthread_cond_timewait(pthread_cond_t *cond, pthread_mutex_t *mutex, const timespec *abstime);

4)唤醒一个等待该条件的线程

int pthread_cond_signal(pthread_cond_t *cond);

5)唤醒所有等待该条件的所有线程

int pthread_cond_broadcast(pthread_cond_t *cond);

6)销毁条件变量

int pthread_cond_destroy(pthread_cond_t *cond);

测试demo:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>

using std::cout;
using std::endl;

pthread_mutex_t mutex;
pthread_cond_t cond;

void *callback1(void *arg)
{
        while(1) {
                pthread_cond_wait(&cond, &mutex);
                cout << "pthread 1 wake up!" << endl;
        }
}

void *callback2(void *arg)
{
        while(1) {
                pthread_cond_wait(&cond, &mutex);
                cout << "pthread 2 wake up!" << endl;
        }
}

void func(int sig)
{
        pthread_cond_signal(&cond);
        // pthread_cond_broadcast(&cond);
}

int main()
{
        signal(15, func);

        pthread_mutex_init(&mutex, NULL);
        pthread_cond_init(&cond, NULL);

        pthread_t tid1, tid2;
        pthread_create(&tid1, NULL, callback1, NULL);
        pthread_create(&tid2, NULL, callback2, NULL);

        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL);

        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&cond);

        return 0;
}

测试结果:

另外一个终端的输入:
killall -15 test2
killall -15 test2
killall -15 test2
killall -15 test2

输出:
pthread 1 wake up!
pthread 2 wake up!
pthread 1 wake up!
pthread 2 wake up!

如果将pthread_cond_signal函数注释,打开pthread_cond_broadcast函数的注释,就可以得到以下的结果:

另外一个终端的输入:
killall -15 test2
killall -15 test2
killall -15 test2
killall -15 test2

输出:
pthread 1 wake up!
pthread 2 wake up!
pthread 1 wake up!
pthread 2 wake up!
pthread 1 wake up!
pthread 2 wake up!
pthread 1 wake up!
pthread 2 wake up!

4、条件变量与互斥锁的关系

测试demo:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>

using std::cout;
using std::endl;

pthread_mutex_t mutex;
pthread_cond_t cond;

void *callback1(void *arg)
{
        while(1) {
                pthread_mutex_lock(&mutex);
                cout << "pthread 1 wait" << endl;
                pthread_cond_wait(&cond, &mutex);
                cout << "pthread 1 wake up!" << endl;
                pthread_mutex_unlock(&mutex);        
        }
}

void *callback2(void *arg)
{
        while(1) {
                pthread_mutex_lock(&mutex);
                cout << "pthread 2 wait" << endl;
                pthread_cond_wait(&cond, &mutex);
                cout << "pthread 2 wake up!" << endl;
                pthread_mutex_unlock(&mutex);        
        }
}

void func(int sig)
{
        pthread_cond_signal(&cond);
        // pthread_cond_broadcast(&cond);
}

int main()
{
        signal(15, func);

        pthread_mutex_init(&mutex, NULL);
        pthread_cond_init(&cond, NULL);

        pthread_t tid1, tid2;
        pthread_create(&tid1, NULL, callback1, NULL);
        pthread_create(&tid2, NULL, callback2, NULL);

        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL);

        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&cond);

        return 0;
}

执行结果如下:

另一个终端:
killall -15 test2
killall -15 test2
killall -15 test2
killall -15 test2

测试结果:
pthread 1 wait
pthread 2 wait
pthread 1 wake up!
pthread 1 wait
pthread 2 wake up!
pthread 2 wait
pthread 1 wake up!
pthread 1 wait
pthread 2 wake up!
pthread 2 wait

pthread_cond_wait函数里面发生了什么?

1)释放了互斥锁

pthread_cond_wait函数里面第二个参数是互斥锁

2)等待条件

3)条件被触发并且给互斥锁加锁

第三步是原子操作

猜你喜欢

转载自blog.csdn.net/qq_58550520/article/details/129089176
今日推荐