我正在嘗試撰寫一個小測驗用例來練習std::shared_timed_mutex::try_lock_until。關于cppreference 的檔案。
這是我的代碼
#include <thread>
#include <iostream>
#include <chrono>
#include <shared_mutex>
#include <cassert>
std::shared_timed_mutex test_mutex;
int global;
void f()
{
auto now=std::chrono::steady_clock::now();
test_mutex.try_lock_until(now std::chrono::seconds(100));
//test_mutex.lock();
--global;
std::cout << "In lock, global=" << global << '\n';
test_mutex.unlock();
}
void g()
{
auto now=std::chrono::steady_clock::now();
test_mutex.try_lock_shared_until(now std::chrono::seconds(10));
//test_mutex.lock_shared();
std::cout << "In shared lock, global=" << global << '\n';
test_mutex.unlock_shared();
}
int main()
{
global = 1;
test_mutex.lock_shared();
std::thread t1(f);
std::thread t2(g);
test_mutex.unlock_shared();
t1.join();
t2.join();
assert(global == 0);
}
我期待的是
- main 獲得讀鎖然后啟動 f 和 g
- f 嘗試獲取排他鎖并阻塞
- g 獲得讀鎖,讀取
global然后解鎖讀鎖 - main 解鎖讀鎖
- f
global解鎖、寫入、解鎖和完成 - f 和 g 連接 7 斷言為真,主要結束
(2 和 3 可以按任何順序排列)。
這本身似乎可以正常作業。在 gdb 下,如果我global在 ing和 write in的讀取上放置斷點f,然后運行,它會在讀取時停止,正如我所期望的那樣。
但是,如果我編譯,-fsanitize=tthread那么我會遇到危險
WARNING: ThreadSanitizer: data race (pid=6780)
Read of size 4 at 0x000000407298 by thread T2:
#0 g() /home/paulf/scratch/valgrind/drd/tests/try_lock_shared_until14.cpp:25 (try_lock_shared_until14 0x402484)
[trimmed]
#6 execute_native_thread_routine ../../../../../libstdc -v3/src/c 11/thread.cc:82 (libstdc .so.6 0xd9c83)
Previous write of size 4 at 0x000000407298 by thread T1:
#0 f() /home/paulf/scratch/valgrind/drd/tests/try_lock_shared_until14.cpp:15 [triimed]
#6 execute_native_thread_routine ../../../../../libstdc -v3/src/c 11/thread.cc:82 (libstdc .so.6 0xd9c83)
Location is global 'global' of size 4 at 0x000000407298 (try_lock_shared_until14 0x000000407298)
在 gdb 下,tsan 版本不會阻塞排他鎖并首先到達寫入。
I realize that my example is not good and I should check return values and not rely on timeouts.
Can anyone explain what tsan is changing? If I use the plain lock/lock_shared/unlock/unlock_shared functions then tsan no longer complains.
(Note that I can't use DRD or Helgrind for this - I'm writing the testcase for them and I know that they do not support this at the moment, at least not on the platform that I'm using, Fedora 34 / GCC 11.2.1 amd64).
Edit:
Here is version 3, which works now. main waits on a cv got g() to finish, then releases the shared lock, then f() can get the exclusive lock.
#include <thread>
#include <iostream>
#include <chrono>
#include <shared_mutex>
#include <mutex>
#include <cassert>
#include <condition_variable>
std::shared_timed_mutex test_mutex;
std::mutex cv_mutex;
std::condition_variable cv;
int global;
bool reads_done = false;
void f()
{
auto now=std::chrono::steady_clock::now();
std::cout << "In lock, trying to get mutex\n";
if (test_mutex.try_lock_until(now std::chrono::seconds(3)))
{
--global;
std::cout << "In lock, global=" << global << '\n';
test_mutex.unlock();
}
else
{
std::cerr << "Lock failed\n";
}
}
void g()
{
auto now=std::chrono::steady_clock::now();
std::cout << "In shared lock, trying to get mutex\n";
if (test_mutex.try_lock_shared_until(now std::chrono::seconds(2)))
{
std::cout << "In shared lock, global=" << global << '\n';
test_mutex.unlock_shared();
}
else
{
std::cerr << "Lock shared failed\n";
}
std::unique_lock<std::mutex> lock(cv_mutex);
reads_done = true;
cv.notify_all();
}
int main()
{
global = 1;
test_mutex.lock_shared();
std::thread t1(f);
std::thread t2(g);
{
std::unique_lock<std::mutex> lock(cv_mutex);
while (!reads_done)
{
cv.wait(lock);
}
}
std::cout << "Main, reader thread done\n";
test_mutex.unlock_shared();
std::cout << "Main, no more shared locks\n";
t1.join();
t2.join();
assert(global == 0);
}
uj5u.com熱心網友回復:
這也是一個有效的調度方案:
- main 獲得讀鎖然后啟動 f 和 g
- main 釋放讀鎖然后加入
- f 開始執行并鎖定超過 10 毫秒,例如由于搶占
- g 開始執行并阻塞 10 ms
- g 解除阻塞并讀取共享變數
在 5. 中,發生了資料競爭,因此 ThreadSanitizer 指出它是正確的。糾正此錯誤需要檢查等的回傳值try_lock_shared_until。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/371852.html
標籤:c multithreading c 14 thread-sanitizer
上一篇:防止多執行緒應用程式在其執行緒之一執行非法操作時終止
下一篇:鍵盤庫阻止了Python執行緒?
