2022-07-01 多线程互斥锁的一个案例

前言

最近回答了一个C语言多线程的小问题.

大概有些初涉多线程编程的学习者,在未学到 “锁” 这个概念的时候就已经开始写多线程代码。

但有个问题,要先明白,对于没有任何规划的多线程,很容易得到与预想相别万里的结果。

因为逻辑变了,单线程是顺序逻辑,多线程是乱序逻辑,需要人为干涉规划执行顺序。

举例

下面是问题的叙述,在无锁情况(并非可无锁并发的数据结构)导致的乱序:

C语言多线程操作多函数冲突

问题遇到的现象和发生背景

通过移动光标的方式,用两个线程执行两个不同函数分别在某列输出不同的数字。由于线程并发,输出的结果不对

问题代码可以参考链接:C语言多线程操作多函数 冲突

程序调用两个线程,运行两个函数:fun1,fun2,这两个函数分别调用 gotoxy 函数,定位光标,然后打印字符。

单线程顺序执行没有问题,多线程有很大问题。

两个线程会乱序的定位光标,乱序打印,导致这样一种情况:A挪光标,还没打印,B打印了,随后A才打印,或者相反,总之不是想要的结果。

为了梳理程序流程,我们需要加锁,让光标移动和打印原子化。

OK,我们在光标移动前和打印后加锁。

现在的问题是,可以有很多地方加锁,具体放哪里?

答案是放在刚刚要定位光标之前,刚刚打印完毕。

这看似是废话,其实还有有一定道理的。

一旦一个进程持有锁,另外一个进程就要等,我可以粗糙的放在 gotoxy 函数之前和 printf 函数之后,没有问题,但是我们想想,gotoxy 内部的变量初始化及句柄获取要不要占时间,它们要占时间,但是和移动光标没有关系,完全可以先准备出来,等持有锁的线程释放后立刻定位光标。

一句话,先把能干的都干了,只等时机到来。多么哲学。

当然这只是一个最简单的用例,基本没有死锁的风险,也没有恶性数据竞争,属于入门的入门。

以下代码:
#include <pthread.h>
#include <stdio.h>
#include <windows.h>

pthread_mutex_t m;

void gotoxy(short x, short y)
{
    COORD pos;
    HANDLE hOutput;
    pos.X = x;
    pos.Y = y;
    hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    pthread_mutex_lock(&m);
    SetConsoleCursorPosition(hOutput, pos);
}

void *fun1(void *unused)
{
    short t = 0;
    while (t < 20)
    {
        gotoxy(10, t++);
        printf("11");
        pthread_mutex_unlock(&m);
    }
    pthread_exit(NULL);
    return NULL;
}

void *fun2(void *unused)
{
    short t = 0;
    while (t < 20)
    {
        gotoxy(1, t++);
        printf("22");
        pthread_mutex_unlock(&m);
    }
    pthread_exit(NULL);
    return NULL;
}

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

    pthread_t pthread1;
    pthread_t pthread2;

    pthread_create(&pthread1, NULL, &fun1, NULL);
    pthread_create(&pthread2, NULL, &fun2, NULL);

    pthread_join(pthread1, NULL);
    pthread_join(pthread2, NULL);

    pthread_mutex_destroy(&m);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不停感叹的老林_<C 语言编程核心突破>

不打赏的人, 看完也学不会.

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值