Linux C——线程同步和线程互斥

线程同步

概念

线程同步是指多个线程在同一时间访问同一资源时,为了保证数据的正确性和一致性,需要对线程的执行顺序进行控制,以保证数据的完整性和正确性。

信号量

信号量的概念

信号量是一个非负整数,其值代表系统中某一资源可供使用的数量,由信号量决定线程是继续执行还是阻塞等待。
信号量是一个受保护的值,只能通过三种方式来访问:初始化、P操作(申请资源)和V操作(释放资源)。

信号量的特点

当信号量大于0时,P操作将信号量减1,并允许一个线程继续执行;当信号量等于0时,P操作阻塞线程,直到其他线程释放了资源;当信号量小于0时,V操作将信号量加1,唤醒一个阻塞的线程。

信号量的分类

1.posix信号量
1)无名信号量(sem_open):只能在同一进程内使用,只能在有名信号量之前使用。
2)有名信号量(sem_init):可以在不同进程间共享,可以在任意时刻使用。
2.system V信号量
也叫信号灯集,属于ICP对象。

相关函数

初始化信号量

ses_init

定义:
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:
    初始化信号量。
参数:
    sem:信号量的地址。
    pshared:信号量共享属性,0表示线程间共享,1表示进程间共享。
    value:信号量的初始值。
返回值:
    成功返回0,出错返回-1。

申请资源

sem_wait

定义:
int sem_wait(sem_t *sem);
功能:
    申请资源,如果信号量大于0,则将信号量减1,并允许一个线程继续执行;否则,阻塞线程,直到其他线程释放了资源。
参数:
    sem:信号量的地址。
返回值:
    成功返回0,出错返回-1。

释放资源

sem_post

定义:
int sem_post(sem_t *sem);
功能:
    释放资源,将信号量加1,唤醒一个阻塞的线程。
参数:
    sem:信号量的地址。
返回值:
    成功返回0,出错返回-1。

销毁信号量

sem_destroy

定义:
int sem_destroy(sem_t *sem);
功能:
    销毁信号量。
参数:
    sem:信号量的地址。
返回值:
    成功返回0,出错返回-1。

线程互斥

概念

线程互斥是指多个线程在同一时间访问同一资源时,只允许一个线程访问,其他线程必须等待,直到该线程访问结束后,其他线程才能访问。

互斥锁

线程同步相当于另一种线程互斥,但是线程互斥不是另一种线程同步,线程同步是按照规定的顺序执行,而线程互斥不需要按顺序执行,只需保证同一时刻只有一个线程来访问共享资源即可。
所有的共享资源只需要一把锁即可达到线程互斥的目的,比如一个人在使用卫生间洗澡时,他只需要关上一道门,别人也无法使用卫生间的其它功能。

函数

pthread_mutex_init

定义:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
功能:
    初始化互斥锁。
参数:
    mutex:互斥锁的地址。
    attr:互斥锁属性,可以为NULL。
返回值:
    成功返回0,出错返回非0。

pthread_mutex_lock

定义:
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:
    申请互斥锁,如果互斥锁已经被其他线程锁定,则阻塞线程,直到互斥锁可用。
参数:
    mutex:互斥锁的地址。
返回值:
    成功返回0,出错返回非0。

pthread_mutex_unlock

定义:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:
    释放互斥锁。
参数:
    mutex:互斥锁的地址。
返回值:
    成功返回0,出错返回非0。

pthread_mutex_destroy

定义:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:
    销毁互斥锁。
参数:
    mutex:互斥锁的地址。
返回值:
    成功返回0,出错返回非0。

死锁

死锁是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

死锁的必要条件

1.互斥
当一个进程或线程在使用资源时,其他进程或线程不能访问该资源。
2.请求和保持
进程或线程在请求其他资源的同时保持原有资源的占有。
3.不剥夺(不可抢占)
资源请求者不能从资源占有者手中夺取资源,只能由资源占有者主动释放资源。
4.环路等待(循环等待)
每个进程都在等待下一个进程所占用的资源,而下一个进程又在等待当前进程所占用的资源,形成一个环路。

条件变量

条件变量是一种同步机制,在某一线程没有接收到条件变量时会发生阻塞,直到另一线程发出条件变量时才会被唤醒。条件变量通常搭配互斥锁一起使用,可以达到线程同步的效果。

函数

pthread_cond_init

定义:
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
功能:
    初始化条件变量。
参数:
    cond:条件变量的地址。
    attr:条件变量属性,可以为NULL。
返回值:
    成功返回0,出错返回非0。

pthread_cond_wait

定义:
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
功能:
    等待条件变量,如果条件变量没有被唤醒,则阻塞线程,直到条件变量被唤醒。
参数:
    cond:条件变量的地址。
    mutex:互斥锁的地址。
返回值:
    成功返回0,出错返回非0。

pthread_cond_signal

定义:
int pthread_cond_signal(pthread_cond_t *cond);
功能:
    唤醒一个等待条件变量的线程。
参数:
    cond:条件变量的地址。
返回值:
    成功返回0,出错返回非0。

pthread_cond_broadcast

定义:
int pthread_cond_broadcast(pthread_cond_t *cond);
功能:
    唤醒所有等待条件变量的线程。
参数:
    cond:条件变量的地址。
返回值:
    成功返回0,出错返回非0。

当没有条件变量产生时,调用pthread_cond_wait会导致线程阻塞,同时会将锁解锁,直到条件变量产生时,线程结束阻塞,同时上锁。pthread_cond_signal只能唤醒一个线程,是一对一的关系。而pthread_cond_broadcast可以唤醒所有等待该条件变量的线程,是一对多的关系。

示例代码:

主线程负责从终端输入金额,子线程循环从输入的金额中扣款,每秒扣100元,扣完又调用主线程输入金额。

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

pthread_mutex_t lock;
pthread_cond_t cond1;
pthread_cond_t cond2;
int balance = 0;
int flag = 0;
void *take_thread(void *arg)
{
    while (1)
    {
        pthread_mutex_lock(&lock);
        if (balance < 100)
        {
            printf("余额不足100,无法取出\n");
            pthread_cond_signal(&cond2);
            pthread_cond_wait(&cond1, &lock);
        }
        balance -= 100;
        printf("成功取出:100\n");
        printf("余额:%d\n", balance);
        sleep(1);
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}
int main(int argc, char const *argv[])
{
    pthread_t tid;
    if (pthread_create(&tid, NULL, take_thread, NULL) != 0)
    {
        perror("thread create error");
        return -1;
    }
    if (pthread_mutex_init(&lock, NULL) != 0)
    {
        perror("lock init error");
        return -1;
    }
    if (pthread_cond_init(&cond1, NULL) != 0)
    {
        perror("cond1 init error");
        return -1;
    }
    if (pthread_cond_init(&cond2, NULL) != 0)
    {
        perror("cond2 init error");
        return -1;
    }
    while (1)
    {
        pthread_mutex_lock(&lock);
        if (balance >= 100)
            pthread_cond_wait(&cond2, &lock);
        printf("请输入金额:");
        scanf("%d", &balance);
        if (balance >= 100)
            pthread_cond_signal(&cond1);
        pthread_mutex_unlock(&lock);
    }
    return 0;
}

由于CPU对线程的调度是随机的,可能输入大于100的金额后会再次执行主线程,所以主线程先要判断金额大于100的话就阻塞,等待子线程扣完款发出条件变量cond2后才可以再次输入。子线程首先需要判断金额是否大于100,小于100的话就向主线程发出条件变量并且阻塞自己,等待主线程输入完且发出条件变量cond1后才可继续执行。
运行结果:

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇