我們已經實作了TaskRunner,其函式將被不同的執行緒呼叫,以啟動、停止和發布任務。TaskRunner將在內部創建一個執行緒,如果佇列不是空的,它將從佇列中彈出任務并執行它。Start() 將檢查執行緒是否正在運行。如果不是,則創建一個新的執行緒。Stop()將加入該執行緒。代碼如下。
bool TaskRunnerImpl::PostTask(任務* task){
tasks_queue_.push_back(任務)。
return true。
}
void TaskRunnerImpl::Start() {
std::lock_guard<std::mutex> lock(is_running_mutex_)。
if(is_running_) {
return;
}
is_running_ = true;
runner_thread_ = std::thread(&TaskRunnerImpl::Run, this)。
}
void TaskRunnerImpl::Run(){
while(is_running_) {
if(tasks_queue_.empty() ) {
continue;
}
任務* task_to_run = tasks_queue_.front();
task_to_run->Run()。
tasks_queue_.pop_front()。
delete task_to_run;
}
}
void TaskRunnerImpl::Stop(){
std::lock_guard<std::mutex> lock(is_running_mutex_)。
is_running_ = false;
if(runner_thread_.joinable() ) {
runner_thread_.join()。
}
這段代碼正在按預期作業。持續的任務被推送,執行緒正在執行這些任務。我們現在想使用條件變數,否則執行緒將持續檢查任務佇列是否為空。我們的實作如下。
- 執行緒函式(Run())將在條件變數上等待。
- PostTask() 將在有人發布任務時發出信號。
- Stop()將在有人呼叫stop時發出信號。
實作的代碼如下。
bool TaskRunnerImpl: :PostTask(Task* task, uint64_t delay_milliseconds){
std::lock_guard<std::mutex> taskGuard(m_task_mutex)。
tasks_queue_.push_back(task)。
m_task_cond_var.notify_one()。
INFO("{} : {} : {}", __FUNCTION__, delay_milliseconds, tasks_queue_.size()。
return true。
}
void TaskRunnerImpl::Start() {
INFO("{}", __FUNCTION__) 。
std::lock_guard<std::mutex> taskGuard(m_task_mutex)。
if(!is_running_) {
is_running_ = true;
runner_thread_ = std::thread(&TaskRunnerImpl::Run, this)。
}
}
void TaskRunnerImpl::Run(){
while(true) {
INFO("{} : {}", __FUNCTION__, 1) 。
{
std::unique_lock<std::mutex> mlock(m_task_mutex)。
INFO("{} : Locked Mutex", __FUNCTION__) 。
m_task_cond_var.wait(mlock, [this]( ) {
INFO("{} : Checking Condition", __FUNCTION__)。
return !(is_running_ && tasks_queue_.empty();
});
INFO("{} : Came out of wait"/span>, __FUNCTION__)。
if(!is_running_) {
return;
}
INFO("{} : Escaped if cond", __FUNCTION__)。
if(!tasks_queue_.empty() ) {
INFO("{} : {} : {}", __FUNCTION__, 2, tasks_queue_. size()); //No LOGs after this GETTING PRINTEDfront();
task_to_run->Run()。
INFO("{} : Deleting Task", __FUNCTION__)。
tasks_queue_.pop_front()。
INFO("{} : After Deletion : {}", __FUNCTION__, tasks_queue_.size() )。
delete task_to_run;
}
INFO("{} : Out of scope", __FUNCTION__)。
}
INFO("{} : 迭代結束", __FUNCTION__)。
}
INFO("{} : returning", __FUNCTION__)。
}
void TaskRunnerImpl::Stop() {
{
std::lock_guard<std::mutex> taskGuard(m_task_mutex)。
is_running_ = false;
INFO("{} : Signalling STOP", __FUNCTION__)。
m_task_cond_var.notify_one()。
}
INFO("{} : {}"/span>, __FUNCTION__, 1)。
if(runner_thread_.joinable() {
runner_thread_.join()。
}
不確定這段代碼有什么問題。我得到了以下輸出。
TaskRunnerImpl.cpp:34: INFO: 啟動
TaskRunnerImpl.cpp:45:INFO: 運行:1。
TaskRunnerImpl.cpp:49:INFO: 運行:鎖定的Mutex
TaskRunnerImpl.cpp:51:INFO: operator() : 檢查條件
TaskRunnerImpl.cpp:29:INFO: PostTask : 0 : 1
TaskRunnerImpl.cpp:29: INFO: PostTask : 0 : 2
TaskRunnerImpl.cpp:51: INFO: operator() : 檢查條件
TaskRunnerImpl.cpp:56:INFO: 運行:從等待中出來
TaskRunnerImpl.cpp:61:INFO: 運行:逃出if條件
TaskRunnerImpl.cpp:63:INFO: 運行:2:2。
這意味著在執行任務之前會列印日志,之后就沒有日志了。通常情況下,PostTask()會被連續呼叫以將任務發布到佇列中。但在新的代碼中,任務運行后沒有日志。所以我假設執行緒函式持有mutex,PostTask()無法將任務推到佇列中。但無法理解為什么執行任務后沒有日志。如果我恢復到原來的代碼,代碼是按預期作業的。誰能告訴我,這段代碼是否有問題。
uj5u.com熱心網友回復:
你可能有一個死鎖。在很多地方,你的代碼保持mutex鎖定的時間遠遠超過要求。
當你啟動執行緒時,你通常不需要一個鎖。
在停止的時候也不需要。你可以為此目的使用一個原子布林值。
最后,當你運行每個任務時(即task_to_run->Run();),你應該明確地沒有鎖。
如果你99%的時間都鎖定了mutex,那么為什么不在主執行緒中做所有事情呢?正如所寫的那樣,你不能在運行任務時發布任何任務,因為突變體被鎖定了。因此,你發布任務的執行緒將等待,直到當前任務完成。
因此,當任務佇列還未清空時,你的發布執行緒將幾乎沒有任何進展。
顯然,你想把最上面的任務放在一個區域變數中,并將該專案從佇列中彈出,然后釋放鎖,再運行該任務。類似于這樣的情況:
if(!tasks_queue_.empty()
{
std::unique_ptr<Task> task_to_run(tasks_queue_.front())。
tasks_queue_.pop_front()。
mlock.unlock()。
task_to_run->Run()。
任何一本好的C 書都應該解釋這個問題。
如果你認真對待多執行緒,那么C Concurrency in action是一本好書。
順便說一下,如果你能使用C 20,你可以通過使用jthread和取消標記來簡化一點代碼。
另一本你可能想讀的書是Concurrency with Modern C 或關于C 20的書。
順便說一下,你的佇列可能也應該存盤std::unique_ptr。否則,你需要清理代碼,在呼叫Stop后洗掉任務。它可以幫助制作安全的例外代碼,同時不需要太多額外的代碼,如明確的try/catch或清理回圈。
甚至bool TaskRunnerImpl::PostTask(Task* task)應該被bool TaskRunnerImpl::PostTask(std::unique_ptr <Task> task)代替,然后你應該std::move你的任務。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/314174.html
標籤:
上一篇:使用矢量時,[]運算子的問題
下一篇:資料型別LongLong是溢位的
