我嘗試寫一些多線程代碼從DAQ設備讀取並呈現在同一時間捕捉的信號:爲什麼我的線程有時會「口吃」?
std::atomic <bool> rendering (false);
auto render = [&rendering, &display, &signal] (void)
{
while (not rendering)
{std::this_thread::yield();};
do {display.draw (signal);}
while (display.rendering()); // returns false when user quits
rendering = false;
};
auto capture = [&rendering, &daq] (void)
{
for (int i = daq.read_frequency(); i --> 0;)
daq.record(); // fill the buffer before displaying the signal
rendering = true;
do {daq.record();}
while (rendering);
daq.stop();
};
std::thread rendering_thread (render);
std::thread capturing_thread (capture);
rendering_thread.join();
capturing_thread.join();
有時這會正常工作,但通常我得到非常糟糕的口吃。我有render()
和capture()
打印在每個循環迭代一行,並再以五色線,以至於紅色爲render()
,藍色是capture()
:
左邊的圖是從一個運行更平穩,右情節是從口吃跑步。
我在C具有大致相當於程序中使用OpenMP和表現得一帆風順:
int status = 0;
#pragma omp parallel num_threads(2) private(tid) shared(status)
/* READ AND DRAW */ {
tid = omp_get_thread_num();
/* DRAW */ if (tid is 0) {
int finished = 0;
while (not finished) {
#pragma omp critical
/* GET JOB STATUS */ {
finished = status;
}
finished = renderDisplay();
}
#pragma omp critical
/* TERMINATE DISPLAY */ {
cvDestroyAllWindows();
}
#pragma omp atomic
status ++;
#pragma omp flush(status)
}
/* READ */ if (tid is 1) {
int finished = 0;
while (not finished) {
#pragma omp critical
/* GET JOB STATUS */ {
finished = status;
}
captureSignal();
}
}
#pragma omp barrier
}
至少,無論是C和C++ 11個版本看等同於我,但我可以不知道爲什麼在C++ 11版本中發生口吃。
我不能發佈SSCCE因爲daq.*
程序都依賴於NI DAQ庫,但它可能是值得注意的是,daq.record()
阻止,直到物理設備讀完,和NI DAQ LIB本身派生幾個線程時開始。
我試過在各種配置中實現原子標誌並更改函數調用順序,沒有任何東西似乎有效果。
這裏發生了什麼,我該如何控制它?
更新:提高採樣率的DAQ減輕了這個問題,這使我強烈懷疑這確實與daq.record()
是一個阻塞調用有關。
只有實時操作系統才能控制程序的流程。試圖控制和預測線程調度將會失敗,如果不是那種OS的話。 – galop1n
真的有必要讓每個線程阻塞另一個線程嗎?一個是生產者,一個是消費者。應該非常直接地使它無鎖(這基本上就是你使用原子的原子) – Brent
原子在這個上下文中不是鎖,它是線程之間的「開始/停止」信號。生產者只會阻止消費者,直到它爲消費者提供足夠的數據才能顯示有意義的東西。之後,線程獨立工作,直到消費者報告用戶想要退出。 –