簡體   English   中英

Win32線程死於無故

[英]Win32 threads dying for no apparent reason

我有一個程序,它產生3個工作線程來進行一些數字運算,並等待它們完成,如下所示:

#define THREAD_COUNT 3
volatile LONG waitCount;
HANDLE pSemaphore;

int main(int argc, char **argv)
{
    // ...

    HANDLE threads[THREAD_COUNT];
    pSemaphore = CreateSemaphore(NULL, THREAD_COUNT, THREAD_COUNT, NULL);
    waitCount = 0;

    for (int j=0; j<THREAD_COUNT; ++j)
    {
        threads[j] = CreateThread(NULL, 0, Iteration, p+j, 0, NULL);
    }
    WaitForMultipleObjects(THREAD_COUNT, threads, TRUE, INFINITE);

    // ...
}

輔助線程在代碼中的某些點使用自定義的屏障函數,以等待所有其他線程到達屏障:

void Barrier(volatile LONG* counter, HANDLE semaphore, int thread_count = THREAD_COUNT)
{
    LONG wait_count = InterlockedIncrement(counter);
    if ( wait_count == thread_count )
    {
        *counter = 0;
        ReleaseSemaphore(semaphore, thread_count - 1, NULL);
    }
    else
    {
        WaitForSingleObject(semaphore, INFINITE);
    }
}

(基於此答案的實現

該程序有時會死鎖。 如果那時我使用VS2008中斷執行並在內部進行深入研究,則Barrier()Wait...行上只有1個工作線程正在Wait... waitCount的值始終為2。

為了使事情變得更加尷尬,線程工作得越快,死鎖的可能性就越大。 如果我在“釋放”模式下運行,則死鎖大約發生10次中的8次。 如果我在“調試”模式下運行,並在線程函數中放置了一些打印件以查看它們的掛起位置,則它們幾乎永遠不會掛起。

因此,我的某些工作線程似乎被提前殺死,而其余線程則停留在屏障上。 但是,線程實際上除了讀取和寫入內存(以及調用Barrier() )外什么也不做,我非常肯定沒有發生段錯誤。 我也可能會得出錯誤的結論,因為(如上面鏈接的問題中所述)我是Win32線程的新手。

這里可能會發生什么,如何使用VS調試這種奇怪的行為?

確切猜測您可能會遇到什么有點困難。 並行編程是(IMO)遵循“保持如此簡單以至於顯然是正確的”這一理念所值得付出的地方之一,不幸的是,我不能說您的Barrier代碼似乎合格。 就個人而言,我想我會有這樣的事情:

// define and initialize the array of events use for the barrier:
HANDLE barrier_[thread_count];

for (int i=0; i<thread_count; i++)
    barrier_[i] = CreateEvent(NULL, true, false, NULL);

// ...

Barrier(size_t thread_num) { 
    // Signal that this thread has reached the barrier:
    SetEvent(barrier_[thread_num]); 

    // Then wait for all the threads to reach the barrier:
    WaitForMultipleObjects(thread_count, barrier_, true, INFINITE);
}

編輯:

好的,既然意圖已經明確了(需要處理多次迭代),我將修改答案,但只做一點修改。 而不是一個事件數組,而是具有兩個:一個用於奇數迭代,一個用於偶數迭代:

// define and initialize the array of events use for the barrier:
HANDLE barrier_[2][thread_count];

for (int i=0; i<thread_count; i++) {
    barrier_[0][i] = CreateEvent(NULL, true, false, NULL);
    barrier_[1][i] = CreateEvent(NULL, true, false, NULL);
}

// ...

Barrier(size_t thread_num, int iteration) { 
    // Signal that this thread has reached the barrier:
    SetEvent(barrier_[iteration & 1][thread_num]); 

    // Then wait for all the threads to reach the barrier:
    WaitForMultipleObjects(thread_count, &barrier[iteration & 1], true, INFINITE);
    ResetEvent(barrier_[iteration & 1][thread_num]);
}

如何調試奇怪的線程行為?

您所說的不完全是,但答案幾乎總是這樣:很好地理解代碼,了解所有可能的結果並確定正在發生的事情。 調試器在這里變得不太有用,因為您可以跟蹤一個線程,錯過導致其他線程失敗的原因,也可以從父線程跟蹤,在這種情況下,執行不再是順序的,並且您到處都是。

現在,解決問題。

pSemaphore = CreateSemaphore(NULL, THREAD_COUNT, THREAD_COUNT, NULL);

MSDN文檔中

lInitialCount [in]:信號量對象的初始計數。 此值必須大於或等於零且小於或等於lMaximumCount。 當信號量的計數大於零時,會發出信號狀態;在信號量為零時,則不會發出信號狀態。 每當等待函數釋放一個正在等待信號量的線程時,計數就會減少一。 通過調用ReleaseSemaphore函數將計數增加指定的數量。

在這里

在線程嘗試執行任務之前,它使用WaitForSingleObject函數來確定信號量的當前計數是否允許它執行此操作。 wait函數的超時參數設置為零,因此,如果信號量處於非信號狀態,該函數將立即返回。 WaitForSingleObject將信號量的計數減一。

因此,我們在這里所說的是,信號量的count參數告訴您一次允許多少個線程執行給定任務。 當您最初將計數設置為THREAD_COUNT您允許所有線程訪問“資源”,在這種情況下將繼續。

您鏈接的答案將這種創建方法用於信號量:

CreateSemaphore(0, 0, 1024, 0)

基本上說沒有一個線程被允許使用該資源。 在您的實現中,信號量會發出信號(> 0),因此一切都會輕松進行,直到其中一個線程設法將計數減少到零為止,這時其他一些線程等待信號量再次發出信號,這可能不是“與您的櫃台同步進行。 記住,當WaitForSingleObject返回時,它會減少信號量上的計數器。

在您發布的示例中,設置:

::ReleaseSemaphore(sync.Semaphore, sync.ThreadsCount - 1, 0);

之所以有效,是因為每個WaitForSingleObject調用都會將信號量的值減小1,並且需要執行threadcount - 1會在threadcount - 1 WaitForSingleObject都返回時發生,因此信號量返回0並因此再次無信號,依此類推下一遍,每個人都在等待,因為不允許任何人一次訪問該資源。

簡而言之,將您的初始值設置為零,看看是否可以解決該問題。


編輯一點解釋:因此以不同的方式思考,信號量就像是n原子門。 您通常會執行以下操作:

// Set the number of tickets:
HANDLE Semaphore = CreateSemaphore(0, 20, 200, 0);

// Later on in a thread somewhere...
// Get a ticket in the queue
WaitForSingleObject(Semaphore, INFINITE); 

// Only 20 threads can access this area 
// at once. When one thread has entered 
// this area the available tickets decrease 
// by one. When there are 20 threads here
// all other threads must wait.

// do stuff

ReleaseSemaphore(Semaphore, 1, 0);
// gives back one ticket.

因此,我們將信號燈用於此處的用途與其設計目的不完全相同。

在您的障礙中,阻止此行的原因是:

*計數器= 0;

當另一個線程執行另一個線程時執行?

LONG wait_count = InterlockedIncrement(計數器);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM