我有一个后台线程用于上传文件。它循环运行;它做了一些工作,然后睡觉直到超时过去或直到通过条件变量明确通知有更多的工作要做。问题是有时候我无法让线程快速退出。
这是一个简化版本:
std::thread g_thread;
std::mutex g_mutex;
std::condition_variable g_cond;
bool g_stop = false;
void threadLoop()
{
while (!g_stop)
{
printf("doing some stuff\n");
std::unique_lock<std::mutex> lock(g_mutex);
g_cond.wait_for(lock, std::chrono::seconds(15));
}
}
int main(int argc, char* argv[])
{
g_stop = false;
g_thread = std::thread(threadLoop);
printf("hello\n");
g_stop = true;
g_cond.notify_one();
g_thread.join();
}
当我运行这个测试程序时,我希望它能够快速退出,但有时它会卡在wait_for()中。我想也许在线程在wait_for()中休眠之前发生了notify_one(),但是在检查了g_stop之后。
是否有一个简单的解决方案,或其他更好的设计模式?
答案 0 :(得分:5)
您正在读取和编写g_stop
变量而没有任何同步(例如使用原子操作,或使用互斥锁来序列化对它的访问)。这是一场数据竞赛,这是一种未定义的行为。
因为你没有安全地访问它,所以允许编译器假设没有其他线程修改threadLoop
,所以在std::atomic<bool>
函数中它可以将它加载到寄存器中一次然后再也不会读取变量,但只是保持循环。
为了确保循环线程看到对变量的写入,您应该使用atomic<bool>
或在对该变量进行所有读/写操作之前锁定互斥锁。如果您使用g_stop
来修复未定义的行为,但不能确保线程不会等待条件变量,因为您建议在检查g_stop = true
的值和之间有一个窗口进入睡眠状态,其中主线程可以设置std::thread g_thread;
std::mutex g_mutex;
std::condition_variable g_cond;
bool g_stop = false;
void threadLoop()
{
std::unique_lock<std::mutex> lock(g_mutex);
while (!g_stop)
{
printf("doing some stuff\n");
g_cond.wait_for(lock, std::chrono::seconds(15));
}
}
int main(int argc, char* argv[])
{
g_stop = false;
g_thread = std::thread(threadLoop);
printf("hello\n");
{
std::lock_guard<std::mutex> lock(g_mutex);
g_stop = true;
}
g_cond.notify_one();
g_thread.join();
}
并发出condvar信号,因此循环线程不会等到notify_one()调用之后,因此错过了它。
这个稍微改变的版本将确保线程不会在条件变量上等待,如果主线程告诉它停止:
g_stop
这是有效的,因为循环线程在检查g_stop = true
时保持对互斥锁的锁定,并且它一直持有该锁,直到它开始在condvar上等待。主线程使用锁来设置g_stop = true
,它只能在另一个线程等待时才能执行。
这意味着现在只有两种可能的执行方式。当线程在condvar上等待时发生g_stop == true
,并且它在notify_one()调用之前唤醒,或者唤醒因为<_ em>的notify_one()调用,但在这两种情况下都是如此它会立即看到[System.ComponentModel.DataAnnotations.Schema.Table("Auditoria")]
public class AuditoriaDAL
{
[Key]
public int AuditoriaId { get; set; }
...
}
并停止循环。