_beginthreadex()线程同步和异步问题

CRT中有一个非常好的多线程函数 _beginthreadex(),通过该函数可以设计同步和异步运行的程序。如下,在别人的程序上改动后,可以更加深入理解线程的同步和异步问题!

---------------

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

CRITICAL_SECTION g_cs;//临界区 锁

typedef struct _STRUCT_DATA_
{
    int id;//用于标识出票id
    int tickets;

}_DATA, *_pDATA;

unsigned __stdcall Fun1Proc(LPVOID lpParam);
unsigned __stdcall Fun2Proc(LPVOID lpParam);


int main()
{
    HANDLE hThread[2] = { NULL,NULL };
    unsigned threadid[2] = { 0 };

    _DATA stru_data;
    stru_data.id = 0;
    stru_data.tickets = 1000;

    InitializeCriticalSection(&g_cs);
    hThread[0] = (HANDLE)_beginthreadex(NULL, 0, Fun1Proc, &stru_data, 0, &threadid[0]);//子线程1
    hThread[1] = (HANDLE)_beginthreadex(NULL, 0, Fun2Proc, &stru_data, 0, &threadid[1]);//子线程2
    Sleep(100);//使其子线程运行一段时间

#if 1
    /*由于是异步结构,主线程继续运行!*/

    while (TRUE)//针对异步问题,通过GetExitCodeThread来获取线程的运行状况,若没有运行完毕,则延时使子线程运行完毕,否则主线程继续等待!
    {
        DWORD dwExitCode1, dwExitCode2;
        GetExitCodeThread(hThread[0], &dwExitCode1);//获取线程是否运行完毕,若运行完毕返回 dwExitCode=0,否则为259
        GetExitCodeThread(hThread[1], &dwExitCode2);
        if (dwExitCode1 != 0 || dwExitCode2 != 0)//有其一未运行完毕,等待100ms
        {
            Sleep(100);
        }
        else
        {
            break;
        }
    }
#endif //通过while循环,可以达到主线程和子线程同步!
    LeaveCriticalSection(&g_cs);

    std::cin.get();
    return 0;
}

unsigned __stdcall Fun1Proc(LPVOID lpParam)
{
    _pDATA data = (_pDATA)lpParam;

    while (TRUE)
    {
        EnterCriticalSection(&g_cs);

        if (data->tickets > 0)
        {
            Sleep(1);
            std::cout << "id: " << data->id++ << std::endl;
            std::cout << "thread1 sell ticket: " << data->tickets-- << std::endl;
            LeaveCriticalSection(&g_cs);
        }
        else
        {
            LeaveCriticalSection(&g_cs);
            break;
        }
        //Sleep(1);//增加一个延时,用于测试共享数据的访问情况
    }

    return 0;
}

unsigned __stdcall Fun2Proc(LPVOID lpParam)
{
    _pDATA data = (_pDATA)lpParam;

    while (TRUE)
    {
        EnterCriticalSection(&g_cs);

        if (data->tickets > 0)
        {
            Sleep(1);
            std::cout << "id: " << data->id++ << std::endl;
            std::cout << "thread2 sell ticket: " << data->tickets-- << std::endl;
            LeaveCriticalSection(&g_cs);
        }
        else
        {
            LeaveCriticalSection(&g_cs);
            break;
        }

    }
    return 0;
}

---------------

以上,通过#if 0 ... #endif,可以设计异步程序,可以发现,在异步条件下,主线程会不管子线程的运行状况,主线程会接着运行,导致最后主线程运行完毕后,子线程还未运行完毕!如图所示:

_beginthreadex()线程同步和异步问题

可以通过 #if 1 ... #endif 来对子线程的运行进行监控,若子线程尚未运行完毕,则让主线程等待一定的时间,若子线程还未运行完毕,则继续等待一段时间,这样可以从代码的角度对主线程和子线程进行同步控制,达到阻塞的目的,当然在其它的线程函数中,有专门的线程阻塞和线程分离函数。如图所示:

_beginthreadex()线程同步和异步问题

需要说明的是,在程序中添加临界区是为了保证每个线程在访问公共数据时不至于窜数据,通过临界区达到互锁的目的,保证数据的安全性,当然mutex函数也可以达到同样的目的。

 

上一篇:Buy Tickets(线段树单点更新,查询某个节点的值)


下一篇:LC 983. Minimum Cost For Tickets