Day3-C语言多线程2-基于线程

本文探讨了pthread_mutex_init函数在多线程中的应用,重点讲解了互斥锁的作用、如何避免race condition,以及在多核环境下假共享问题的出现。通过example5.c和example5s.c的对比,揭示了如何通过正确使用锁和减少数据共享来提升程序效率。
摘要由CSDN通过智能技术生成

互斥锁

时间:2021年7月19日14:11:44
pthread_mutex_init函数
SYNOPSIS
       #include <pthread.h>

       int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);
           
       pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

argument1:互斥锁的ID地址
argument2:指定新建互斥锁的属性,NULL,默认属性为快速互斥锁,PTHREAD_PROCESS_PRIVATE 是其缺省值。

互斥锁ID:每一个互斥锁都有一个互斥锁ID。ID类型是pthread_mutex_t

example4s.c

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

//声明一个锁
pthread_mutex_t lock;

int s;

void *myFunc(void *args)
{
    for (int i = 1; i <= 100000; i++)
    {
        pthread_mutex_lock(&lock); //上锁
        s++;
        pthread_mutex_unlock(&lock); //解锁
    }

    return NULL;
}

int main()
{
    pthread_t th1;
    pthread_t th2;

    //对锁进行初始化,即创建一个锁
    pthread_mutex_init(&lock, NULL);

    pthread_create(&th1, NULL, myFunc, NULL);
    pthread_create(&th2, NULL, myFunc, NULL);

    //等待线程执行完毕
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);

    printf("s = %d\n", s);

    return 0;
}

运行结果:

s = 200000

//声明一个锁
pthread_mutex_t lock;


//对锁进行初始化,即创建一个锁
pthread_mutex_init(&lock,NULL);

pthread_mutex_lock(&lock);//上锁
/*需要上锁的代码*/
pthread_mutex_unlock(&lock);//解锁

当一个以上线程需要用同一部分代码时,为避免资源竞争问题(race condition),将相关代码锁起来;
先到先使用,若线程1先到,使用时先上锁,晚到的线程2等待线程1使用完,打开锁,才能使用(有点像排队上厕所).

多线程效率问题

多核的情况下,会出现假共享(false sharing)

example5.c

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

#define MAX_SIZE 50000000

//线程参数结构体
typedef struct
{
    int first;
    int last;
    int result;//存结果
} MY_ARGS;

int *arr;

void *myFunc(void *args)
{
    int s = 0;
    MY_ARGS *my_args = (MY_ARGS *)args;
    int first = my_args->first;
    int last = my_args->last;

    for (int i = first; i < last; i++)
    {
        s += arr[i];
    }
    my_args->result = s;

    return NULL;
}

int main()
{
    arr = malloc(sizeof(int) * MAX_SIZE);
    for (int i = 0; i < MAX_SIZE; i++)
    {
        arr[i] = rand() % 5; //区间[0, 4],防止结果溢出
    }

    pthread_t th1;
    pthread_t th2;

    int mid = MAX_SIZE / 2;
    MY_ARGS arg1 = {0, mid, 0};
    MY_ARGS arg2 = {mid, MAX_SIZE, 0};

    //创建两个线程
    pthread_create(&th1, NULL, myFunc, &arg1);
    pthread_create(&th2, NULL, myFunc, &arg2);

    //等待线程执行完毕
    pthread_join(th1, NULL);
    pthread_join(th2, NULL);

    int s1 = arg1.result;
    int s2 = arg2.result;
    printf("s1 = %d\n", s1);
    printf("s2 = %d\n", s2);
    printf("s1 + s2 = %d\n", s1 + s2);

    return 0;
}

运行结果:

s1 = 50010613
s2 = 50004629
s1 + s2 = 100015242

real	0m0.480s
user	0m0.462s
sys	0m0.064s
s1 = 50010613
s2 = 50004629
s1 + s2 = 100015242

real	0m0.486s
user	0m0.508s
sys	0m0.024s

example5s.c

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

#define MAX_SIZE 50000000

//线程参数结构体
typedef struct
{
    int first;
    int last;
    int id;//线程ID
} MY_ARGS;

int *arr;
int results[2] = {0, 0};//存结果

void *myFunc(void *args)
{
    MY_ARGS *my_args = (MY_ARGS *)args;
    int first = my_args->first;
    int last = my_args->last;
    int id = my_args->id;

    for (int i = first; i < last; i++)
    {
        results[id] += arr[i];
    }

    return NULL;
}

int main()
{
    arr = malloc(sizeof(int) * MAX_SIZE);
    for (int i = 0; i < MAX_SIZE; i++)
    {
        arr[i] = rand() % 5; //区间[0, 4],防止结果溢出
    }

    pthread_t th1;
    pthread_t th2;

    int mid = MAX_SIZE / 2;
    MY_ARGS arg1 = {0, mid, 0};
    MY_ARGS arg2 = {mid, MAX_SIZE, 1};

    pthread_create(&th1, NULL, myFunc, &arg1);
    pthread_create(&th2, NULL, myFunc, &arg2);

    pthread_join(th1, NULL);
    pthread_join(th2, NULL);

    printf("s1 = %d\n", results[0]);
    printf("s2 = %d\n", results[1]);
    printf("s1 + s2 = %d\n", results[0] + results[1]);

    return 0;
}

运行结果:

s1 = 50010613
s2 = 50004629
s1 + s2 = 100015242

real	0m0.774s
user	0m1.038s
sys	0m0.044s

s1 = 50010613
s2 = 50004629
s1 + s2 = 100015242

real	0m0.774s
user	0m1.028s
sys	0m0.068s

程序example5s.c明显比程序example5.c运行速度更慢:

example5s.c程序发生了假共享(false sharing)


多核处理多个线程时,会将数组results的数据分别读入自己的缓存.

  1. 核1处理线程1,读取数组results,修改results[0]的值 ;

  2. 同时,核2处理线程2,读取数组results,修改results[1]的值;

  3. 线程1处理好results[0]写回原数组时,发现核2已经将处理好的results[1]写回去了,线程1中的results[1]和原数组中的不相同,于是将原数组中修改过的results[1]读入核1的results[1]中,再将数组results整个写回原数组.

    (也可能是线程1先处理好,线程2遇到false sharing)


在这里插入图片描述

example5.c中的s一直都在CPU中,线程交替运行不会重复读写.

而example5s.c的results[0],results[1]都不是CPU中的,线程交替运行时都有读入写入操作.

频繁的读写造成时间的浪费.


多线程到此告一段落.
我感觉下方链接介绍的还可以 – 推荐链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值