您的位置:首页 > 编程语言

Windows核心编程笔记 用户模式下的线程同步

2014-07-31 12:40 274 查看
Interlock系列函数
操作具有原子性。具体详见《windows核心编程第8章》

Interlock函数用在旋转锁中的情况:
//global variable indicating whether a shared resource is in use or not

BOOL g_fResourceInUse = FALSE; ...

void Func1(){

    //wait to access the resource.

    while( InterlockedExchange(&g_fResourceInUse, TRUE) == TRUE ){

        Sleep(0);

    }

    

    //access the resource

    ...

    //We no longer need to access the resource

    InterlockedExchange( &g_fResourceInUse, FALSE );

}
while会不停地运行,把g_fResourceInUse的值设置为TRUE并检查原来的值是否为TRUE。如果原来的值为FALSE,那说明资源尚未被使用,于是调用线程立刻就能将它设置为“使用中”,然后退出循环。如果原来的值是TRUE,那说明其他线程正在使用该资源,于是while将继续执行。

条件变量
让线程以原子方式把锁释放并将自己阻塞。
SleepConditionVariableCS(...)    //用于释放关键段,并等待在某个条件变量上。
SleepConditionVariableSRW(...)   //用于释放读写锁,并等待在某个条件变量上。
当条件满足的时候,可以调用Wake函数将沉睡的等待唤醒:
WakeConditionVariable(...)       //唤醒一个等待的线程,让它得到锁,并返回。当这个线程释放同一个锁的时候,不会唤醒其他正在等待同一个条件变量的线程。
WakeAllConditionVariable(...)    //唤醒所有等待的线程

停止线程时的死锁问题
void StopProcessing(){
    if(!g_fShutdown){
        InterlockedExchangePointer((PLONG*)&g_fShutdown, (LONG)TRUE);
        
        //Free all threads waiting on condition variables
        WakeAllConditionVariable(&g_cvReadyToConsume);
        WakeAllConditionVaraible(*g_cvReadyToProduce);
        
        //Wait for all the threads to terminate & then clean up.
        WaitForMultipleObjects(g_nNumThreads, g_hThreads, TRUE, INFINITE);
        
        //Don't forget to clean up kernal resources
        //Note: This is not really mandatory since the process is exiting.
        while(g_nNumThreads--){
            CloseHandle(g_hThreads[g_nNumThreads]);

        //Close each list box
        AddText(GetDlgItem(g_hwnd, IDC_SERVERS), TEXT("--------------"));
        AddText(GetDlgItem(g_hwnd, IDC_CLIENTS), TEXT("--------------"));
        }
    }
}
代码先把g_fShutdown标志设为TURE,接着调用WakeAllConditionVariable来触发两个条件变量,然后调用WaitForMultipleObjects,把所有正在运行的线程的句柄在一个数组参数内传入。当WaitForMultipleObjects返回时,所有线程的句柄都已关闭,代码把两个列表框的最后一行添加到列表框中。

在客户/服务器这边,一旦因为WakeAllConditionVariable而把它们从SleepConditionVariableSRW调用唤醒,照道理这些线程应该会监视g_fShutdown标志并在各自的列表框中显示"bye-bye"后直接退出。死锁正是发生在当线程向列表框发送消息的时候。如果执行StopProcessing函数的代码位于WM_COMMAND消息处理函数的内部,那么负责处理消息的用户界面线程会阻塞在WaitForMultipleObjects函数中。因此,当其中一个客户线程或服务器线程在调用ListBox_SetCurSel和ListBox_AddString的时候——死锁了。

所以千万不要让界面线程阻塞,然后还在往界面上发送消息。

一些有用的窍门和技巧:
1. 以原子方式操作一组对象时,使用一个锁。
2. 同时访问多个逻辑资源(例如我们要访问两个或者更多个资源的时候),这些资源如果都分别有自己的锁,那么获取这些锁的顺序一定要一致。否则会出现A线程拥有1号资源等2号资源,而B线程拥有2号资源等待1号资源的死锁情况。
3. 不要长时间占用锁。
下面的代码会在WM_SOMEMSG消息被发送到另一个窗口之前,需要阻止其他线程修改g_s的值。
SOMESTRUCT g_s;
CRITICAL_SECTION g_cs;
DWORD WINAPI SomeThread(PVOID pvParam){
    EnterCriticalSection(&g_cs);
    
    //send a message to a window.
    SendMessage(hWndSomeWnd, WM_SOMEMSG, &g_s, 0);
    
    LeaveCriticalSection(&g_cs);
    
    return 0;
}

我们不能知道窗口过程需要多长时间来处理WM_SOMEMSG消息。可能几毫秒,也有可能几年。所以下面的方法更好些。

SOMESTRUCT g_s;
CRITICAL_SECTION g_cs;
DWORD WINAPI SomeThread(PVOID pvParam){
    EnterCriticalSection(&g_cs);
    SOMESTRUCT sTmp = g_s;
    LeaveCriticalSection(&g_cs);

    //send a message to a window.
    SendMessage(hWndSomeWnd, WM_SOMEMSG, &sTmp,
0);
     
 
    return 0;
}

以上代码可能保证我们尽量少的时间占用锁。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  windows 编程