线程的同步和互斥

线程的同步:

线程间同步(Thread Synchronization)是指在多线程环境下,通过使用信号量(Semaphore)、条件变量(Condition Variable)或其他同步机制,确保多个线程按照一定的顺序执行,以达到协调和合作的目的。同步机制可以用于控制线程的执行顺序、等待其他线程的完成、传递消息等。通过同步机制,可以避免线程之间的竞争条件和数据不一致问题。

信号量的操作:

p操作:sem_wait (sem_t *sem)申请资源,信号量减一

v操作:sem_post()释放资源,信号量加1

PV操作通常用于实现信号量(Semaphore),而信号量是一种用于线程间同步和互斥的机制。

PV操作包括两个基本操作:

1. P操作(也称为wait操作):P操作用于申请资源。当线程执行P操作时,如果资源可用(信号量的值大于0),线程将继续执行,并将信号量的值减1,表示占用了一个资源。如果资源不可用(信号量的值为0),线程将被阻塞,等待资源的释放。

2. V操作(也称为signal操作):V操作用于释放资源。当线程执行V操作时,它将释放一个资源,并将信号量的值加1。如果有其他线程正在等待该资源,其中一个线程将被唤醒,继续执行。

PV操作可以用于解决多线程环境下的资源竞争和互斥访问问题。通过使用信号量和PV操作,可以实现对共享资源的控制和同步,确保线程之间的正确执行顺序和共享资源的一致性。

需要注意的是,PV操作是原子操作,即在执行过程中不会被中断。这确保了线程在执行PV操作时的原子性和互斥性,避免了竞态条件和数据不一致的问题。

理解信号量:信号量与资源的关系

在多线程编程中,信号量是一种用于控制对共享资源访问的同步机制。信号量可以看作是一种计数器,用于表示可用的资源数量。

资源可以是任何需要被线程竞争和访问的共享对象,例如共享内存区域、文件、设备等。在代码中,数组num可以被视为一个共享资源。

信号量的值表示可用的资源数量。当信号量的值大于0时,表示有可用的资源。当信号量的值为0时,表示资源已被占用,其他线程需要等待。

通过使用信号量,可以控制线程对共享资源的访问。当一个线程需要访问共享资源时,它会尝试获取信号量资源(通过调用sem_wait()函数)。如果信号量的值大于0,线程可以获取资源并继续执行。如果信号量的值为0,线程将被阻塞,直到有其他线程释放信号量资源。

当一个线程完成对共享资源的访问时,它需要释放信号量资源,以便其他线程可以获取资源并继续执行(通过调用sem_post()函数)。释放信号量资源会将信号量的值加1,并唤醒等待该信号量的线程。

因此,信号量是一种用于控制对共享资源访问的机制,通过调整信号量的值来控制线程的执行顺序和互斥访问共享资源。

可以理解为只有当信号量资源非0的时候,对应线程才能执行

总结:

sem_wait()函数用于获取信号量资源,如果信号量的值大于0,则将信号量的值减1,并继续执行。如果信号量的值为0,则线程将被阻塞,直到信号量的值大于0为止

sem_post()函数用于释放信号量资源,也称为V操作。它会将信号量的值加1,并唤醒一个等待该信号量的线程(如果有)。

sem_wait()函数用于获取信号量资源,也称为P操作。它会尝试获取信号量,如果信号量的值大于0,则将信号量的值减1,并继续执行。如果信号量的值为0,则线程将被阻塞,直到信号量的值大于0为止。

sem_post()函数用于释放信号量资源,也称为V操作。它会将信号量的值加1,并唤醒一个等待该信号量的线程(如果有)。

在代码中,sem_wait(&sem_sort)用于获取sem_sort信号量资源,以确保在适当的时候执行排序操作。sem_post(&sem_trans)用于释放sem_trans信号量资源,以通知逆序线程可以执行逆序操作。sem_post(&sem_show)用于释放sem_show信号量资源,以通知打印线程可以执行打印操作。

通过使用sem_wait()和sem_post()函数,可以控制线程对共享资源的访问,确保线程按照预期的顺序执行,并避免竞争条件和数据不一致的问题。

1、信号量的设置

首先使用sem_t定义全局信号量变量

然后使用信号量初始化函数设置信号量的值

sem_init函数用于初始化一个命名或未命名的信号量。它的原型如下:

int sem_init(sem_t *sem, int pshared, unsigned int value);

- sem:指向要初始化的信号量的指针。
- pshared:指定信号量的共享方式。如果为0,则信号量只能在同一进程的线程之间共享。如果为非零值,则信号量可以在多个进程之间共享。
- value:指定信号量的初始值。

sem_init函数成功返回0,失败返回-1,并设置errno来指示错误类型。

/*
 *                        _oo0oo_
 *                       o8888888o
 *                       88" . "88
 *                       (| -_- |)
 *                       0\  =  /0
 *                     ___/`---'\___
 *                   .' \\|     |// '.
 *                  / \\|||  :  |||// \
 *                 / _||||| -:- |||||- \
 *                |   | \\\  - /// |   |
 *                | \_|  ''\---/''  |_/ |
 *                \  .-\__  '-'  ___/-. /
 *              ___'. .'  /--.--\  `. .'___
 *           ."" '<  `.___\_<|>_/___.' >' "".
 *          | | :  `- \`.;`\ _ /`;.`/ - ` : | |
 *          \  \ `_.   \_ __\ /__ _/   .-` /  /
 *      =====`-.____`.___ \_____/___.-`___.-'=====
 *                        `=---='
 *
 *
 *      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *            佛祖保佑     永不宕机     永无BUG
 */
// 使用多线程(分别实现对同一数组进行冒泡排序、逆序、打印)保证输出数组要么是升序要么是降序。

#include <pthread.h>
#include <semaphore.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
// 定义信息量全局变量

sem_t sem_sort;
sem_t sem_trans;
sem_t sem_show;

// 排序函数
void *sort(void *arg)
{
    int *num = (int *)arg;
    // 执行排序
    sem_wait(&sem_sort); // 为1表示该资源能被线程使用使用后,其值会减1,为0就表示该该线程暂无资源可以使用,阻塞
    for (int i = 0; i < 10 - 1; i++)
    {
        for (int j = 0; j < 10 - 1 - i; j++)
        {
            int temp;
            if (num[j] > num[j + 1])
            {
                temp = num[j];
                num[i] = num[j + 1];
                num[j + 1] = temp;
            }
        }
    }
    sem_post(&sem_trans); // 对信号量资源加1,通知trans线程,资源可用
}
// 逆序函数
void *trans(void *arg)
{
    int *num = (int *)arg;
    sem_wait(&sem_trans);
    int i = 0;
    int j = 9;
    int temp;
    while (i < j)
    {
        temp = num[i];
        num[i] = num[j];
        num[j] = temp;
        i++;
        j--;
    }
    sem_post(&sem_show);
}
// 打印函数
void *show(void *arg)
{
    int *num = (int *)arg;
    sem_wait(&sem_show);
    printf("The array is: ");
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", num[i]);
    }
    printf("\n");
    return NULL;
}

int main()
{
    srand(time(NULL));
    // 创建一个数组用于存储一个数据,传参的时候把地址传过去
    int num[10];
    for (int i = 0; i < 10; i++)
    {
        num[i] = rand() % 100;
    }
    // 信息量初始化
    if (-1 == sem_init(&sem_sort, 0, 1))
    {
        perror("pthread error");
    }
    if (-1 == sem_init(&sem_trans, 0, 0))
    {
        perror("pthread error");
    }
    if (-1 == sem_init(&sem_show, 0, 0))
    {
        perror("pthread error");
    }
    // 线程创建:1、定义线程tid
    pthread_t tid_sort;
    pthread_t tid_trans;
    pthread_t tid_show;
    // 开辟线程:
    if (0 != pthread_create(&tid_sort, NULL, sort, num))
    {
        perror("pthread_create error ");
    }

    if (0 != pthread_create(&tid_trans, NULL, trans, num))
    {
        perror("pthread_create error ");
    }
    if (0 != pthread_create(&tid_show, NULL, show, num))
    {
        perror("pthread_create error ");
    }
    // 同步线程
    if (0 != pthread_join(tid_sort, NULL))
    {
        perror("jion error");
        return -1;
    }
    if (0 != pthread_join(tid_trans, NULL))
    {
        perror("jion error");
        return -1;
    }
    if (0 != pthread_join(tid_show, NULL))
    {
        perror("jion error");
        return -1;
    }
}

线程间互斥

线程间互斥(Mutual Exclusion)是指在多线程环境下,通过使用互斥锁(Mutex)或其他同步机制,确保同一时间只有一个线程可以访问共享资源。当一个线程获得了互斥锁后,其他线程需要等待,直到该线程释放互斥锁。互斥锁可以防止多个线程同时访问共享资源,从而避免了数据竞争和不一致的问题。

对于线程同步和线程互斥之间的联系:

如果线程按照一定的顺序执行,并且每个线程在访问共享资源时都不会被其他线程中断,那么确实可以避免数据竞争和不一致的问题。然而,在实际的多线程编程中,线程的执行顺序和时间是不确定的,可能会被操作系统的调度器随时中断和调度,这就可能导致数据竞争和不一致的问题。

让我们考虑一个更复杂的例子,假设有一个在线银行应用,有两个线程分别代表两个不同的用户。这两个用户都想从他们共享的银行账户中取款。

假设账户的初始余额为100美元。用户A想取款50美元,用户B想取款70美元。每个取款操作都包括两个步骤:检查余额是否足够,如果足够则减去取款金额。

如果没有使用互斥锁来保护账户余额,可能会出现以下的情况:

1. 用户A的线程检查余额,发现足够。
2. 用户B的线程也检查余额,发现足够。
3. 用户A的线程从账户中取款50美元,余额变为50美元。
4. 用户B的线程也从账户中取款70美元,余额变为-20美元。

在这种情况下,即使每个用户都检查了余额是否足够,但由于没有使用互斥锁来保护账户余额,所以最终的余额变为了负数,这显然是不正确的。

如果使用互斥锁来保护账户余额,那么在任何时候只有一个用户可以访问账户。这样,即使有多个用户需要取款,也不会出现余额不足的问题。

示例代码如下:

pthread_mutex_t mutex; // 定义互斥锁
int balance = 100; // 账户余额

void *withdraw(void *amount)
{
    int amt = *(int *)amount;
    pthread_mutex_lock(&mutex); // 加锁
    if (balance >= amt) {
        balance -= amt;
    }
    pthread_mutex_unlock(&mutex); // 解锁
}

通过这种方式,你可以在已经使用了线程同步的同时,使用线程互斥来保护共享资源,确保数据的一致性。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值