c++多线程二

线程同步

线程同步是指在多线程编程中,为了保证多个线程按照某种特定的方式正确、有序地执行,需要进行线程间的协作与同步。在多线程编程中,当多个线程共享同一份资源时,由于线程的执行顺序是不确定的,因此会存在一些并发问题,如死锁、竞态条件、资源争用等问题。为了避免这些问题,需要对线程进行同步。线程同步实际上就是通过线程之间的协作,使得线程能够按照一定的顺序来访问共享资源,从而避免并发问题的发生。常用的线程同步机制有互斥锁、信号量、条件变量等。

线程不同步可能会导致以下问题

  1. 竞态条件(Race Condition):多个线程同时访问、修改一份共享资源,可能会导致资源的状态不确定,进而导致程序出现逻辑错误,甚至崩溃。

  2. 死锁(Deadlock):多个线程在等待对方释放锁,导致所有线程都无法继续执行,程序陷入死循环,最终可能会崩溃。

  3. 饥饿(Starvation):某些线程可能因为无法获取资源而一直等待,导致无法正常执行,进而影响整个程序的性能。

  4. 资源争用(Resource Contention):多个线程同时竞争同一份资源,导致资源的使用效率下降,总体性能降低。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
 
#define MAX 50
// 全局变量
int number;
 
// 线程处理函数
void* funcA_num(void* arg)
{
    for(int i=0; i<MAX; ++i)
    {
        int cur = number;
        cur++;
        usleep(10);
        number = cur;
        printf("Thread A, id = %lu, number = %d\n", pthread_self(), number);
    }
 
    return NULL;
}
 
void* funcB_num(void* arg)
{
    for(int i=0; i<MAX; ++i)
    {
        int cur = number;
        cur++;
        number = cur;
        printf("Thread B, id = %lu, number = %d\n", pthread_self(), number);
        usleep(5);
    }
 
    return NULL;
}
 
int main(int argc, const char* argv[])
{
    pthread_t p1, p2;
 
    // 创建两个子线程
    pthread_create(&p1, NULL, funcA_num, NULL);
    pthread_create(&p2, NULL, funcB_num, NULL);
 
    // 阻塞,资源回收
    pthread_join(p1, NULL);
    pthread_join(p2, NULL);
 
    return 0;
}
 

通过对上面例子的测试,可以看出虽然每个线程内部循环了 50 次每次数一个数,但是最终没有数到 100,通过输出的结果可以看到,有些数字被重复数了多次,其原因就是没有对线程进行同步处理,造成了数据的混乱。

线程同步方式

  1. 互斥锁(Mutex):使用互斥锁来控制多个线程对共享资源的访问。只有获得锁的线程才能进入临界区进行操作,其他线程必须等待锁释放后才能进入。

  2. 读写锁(Read-Write Lock):允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。读写锁可以提高程序的并发性能,减少资源争用问题。

  3. 条件变量(Condition Variable):用于等待特定条件的发生。当某个线程等待某个条件变量时,它会被阻塞,直到其他线程发出信号,通知条件变量已经满足。

  4. 信号量(Semaphore):使用信号量来控制多个线程对有限数量资源的访问。信号量表示资源的数量,每个线程在使用完资源后必须释放信号量,以便其他线程可以使用资源。

以上是常见的几种线程同步方式,每种方式都有其适用的场景和优缺点,根据具体的应用场景选择适合的同步方式是非常重要的,这里先介绍互斥锁。

对上面的代码加互斥锁:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>

#define MAX 50
// 全局变量
int number;

// 创建一把互斥锁
// 全局变量, 多个线程共享
pthread_mutex_t mutex;

// 线程处理函数
void* funcA_num(void* arg)
{
    for(int i=0; i<MAX; ++i)
    {
        // 如果线程A加锁成功, 不阻塞
        // 如果B加锁成功, 线程A阻塞
        pthread_mutex_lock(&mutex);
        int cur = number;
        cur++;
        usleep(10);
        number = cur;
        pthread_mutex_unlock(&mutex);
        printf("Thread A, id = %lu, number = %d\n", pthread_self(), number);
    }

    return NULL;
}

void* funcB_num(void* arg)
{
    for(int i=0; i<MAX; ++i)
    {
        // a加锁成功, b线程访问这把锁的时候是锁定的
        // 线程B先阻塞, a线程解锁之后阻塞解除
        // 线程B加锁成功了
        pthread_mutex_lock(&mutex);
        int cur = number;
        cur++;
        number = cur;
        pthread_mutex_unlock(&mutex);
        printf("Thread B, id = %lu, number = %d\n", pthread_self(), number);
        usleep(5);
    }

    return NULL;
}

int main(int argc, const char* argv[])
{
    pthread_t p1, p2;

    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    // 创建两个子线程
    pthread_create(&p1, NULL, funcA_num, NULL);
    pthread_create(&p2, NULL, funcB_num, NULL);

    // 阻塞,资源回收
    pthread_join(p1, NULL);
    pthread_join(p2, NULL);

    // 销毁互斥锁
    // 线程销毁之后, 再去释放互斥锁
    pthread_mutex_destroy(&mutex);

    return 0;
}


互斥锁使得一段处于临界区的代码只能在同一时刻被一个线程使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值