多线程的那些事-03

Posted 程序员加油站

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程的那些事-03相关的知识,希望对你有一定的参考价值。

◆ ◆ ◆  ◆ 

前期内容


上一篇《多线程的那些事-02》中我们写了一个简单的多线程程序,然后发现运行中出现全局变量数值不同步的现象,然后深入分析了产生这种现象的原因,并引出了"原子操作"的概念。那么,这一篇我们就看看Windowsd的API提供给了我们哪些可用的原子操作函数。

使用“原子函数”解决同步问题

首先回到之前我们写的那个小程序中,为了测试现象更容易观察,让程序重复运行50次。

#include <stdio.h>
#include <windows.h>
#include <process.h>

volatile long g_nLogin = 0;                 //登录次数
const DWORD THREAD_NUM = 50;                //启动线程数

unsigned int __stdcall ThreadFun(LPVOID pPM)
{
    Sleep(1000);
    g_nLogin++;
    Sleep(50);
    return 0;
}

int main()
{
    HANDLE  handle[THREAD_NUM];
    for (size_t i = 0; i < 50; i++)
    {
        g_nLogin = 0;
        for (int i = 0; i < THREAD_NUM; i++)
            handle[i] = (HANDLE)_beginthreadex(NULL0, ThreadFun, NULL0NULL);
        WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
        printf("%d个线程运行后,记录结果: %d ", THREAD_NUM, g_nLogin);
    }
    return 0;
}

运行结果:

多线程的那些事-03

通过我们在《多线程的那些事-02》中的分析知道,程序运行出现的问题就在于线程函数ThreadFun中的g_nLogin++。换言之,如果这条语句是原子操作,那么程序就不会出现Bug。幸运的是windows给用户提供了一系列这样的函数。现在我们用下面的这个原子函数替换我们程序中的g_nLogin++。

LONG __cdecl InterlockedIncrement(LONG volatile* Addend)

替换之后的代码:

#include <stdio.h>
#include <windows.h>
#include <process.h>

volatile long g_nLogin = 0;                 //登录次数
const DWORD THREAD_NUM = 50;                //启动线程数

unsigned int __stdcall ThreadFun(LPVOID pPM)
{
    Sleep(1000);
    InterlockedIncrement(&g_nLogin);
    Sleep(50);
    return 0;
}

int main()
{
    HANDLE  handle[THREAD_NUM];
    for (size_t i = 0; i < 50; i++)
    {
        g_nLogin = 0;
        for (int i = 0; i < THREAD_NUM; i++)
            handle[i] = (HANDLE)_beginthreadex(NULL0, ThreadFun, NULL0NULL);
        WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
        printf("%d个线程运行后,记录结果: %d ", THREAD_NUM, g_nLogin);
    }
    return 0;
}

运行结果:

多线程的那些事-03

对比之前的运行结果可以发现,使用原子操作很好的解决了多线程引入的问题。聪明的小伙伴可能就有疑问了,在线程函数中我们并不是都是用进行自增++这样的操作,可能里面还有很复杂的逻辑呢!那怎么解决呢?

Windows的同步函数

对于比较简单的操作,windows提供给了我们类似InterlockedIncrement这样的函数,对于我们自己要实现的逻辑就需要我们使用线程同步相关的函数来自己控制了。对于线程的同步我们后面一点点来探讨,这里我们来 看看Windows给我们提供了哪些原子操作的函数。

Interlocked系列函数(完整内容可点击访问https://msdn.microsoft.com/zh-cn/library/aa909196.aspx)

上表中都是Windows提供的用于同步的函数。这里我们主要去关注Interlocked开头的这几个函数。

LONG__cdecl InterlockedIncrement(LONG volatile* Addend)
LONG__cdecl InterlockedDecrement(LONG volatile* Addend)
LONG__cdecl InterlockedExchangeAdd(LONG volatile* Addend, LONGValue)
  1. 变量赋值操作

LONG__cdecl InterlockedExchange(LONG volatile* Target, LONGValue)

好了,这篇内容就到这里了,小伙伴们快去试试上面的集合函数吧,有问题可关注微信留言哟。下一篇开始我们就来重点关注多线程的同步问题,同步问题是多线程中最为核心的部分,也是程序员内功深厚的体现。快来一起成长吧.


以上是关于多线程的那些事-03的主要内容,如果未能解决你的问题,请参考以下文章

java多线程那些事之中的一个

Java基础__Java中多线程那些事

文件句柄文件描述符与进程和多线程的那些事

python并发线程那些事

Java线程池的那些事

提起线程,你不了解的那些事