C++ 安全线程终止

C++ 安全线程终止,c++,windows,multithreading,visual-c++,thread-safety,C++,Windows,Multithreading,Visual C++,Thread Safety,工作线程处理数据库更新。当应用程序结束时,我想终止它,但不让它留下不一致的db。因此,我在每个重要的db操作之前使用标志。过了一会儿,代码中充满了if: void MyWorkerThread::RunMethod(){ if (false == m_Manager->GetShutdownFlag()) { DoSomethingOnDB(); } if (false == m_Manager->GetShutdow

工作线程处理数据库更新。当应用程序结束时,我想终止它,但不让它留下不一致的db。因此,我在每个重要的db操作之前使用标志。过了一会儿,代码中充满了if:

      void MyWorkerThread::RunMethod(){
      if (false == m_Manager->GetShutdownFlag()) {
         DoSomethingOnDB();
      }
      if (false == m_Manager->GetShutdownFlag()) {
         DoSomethingMoreOnDB();
      }
      ...

那么,有没有其他方法可以在不发送大量if的情况下处理关机问题呢?

您最好通过一些同步对象(事件、锁、互斥、信号量、关键部分等)或诸如此类的对象来同步线程,以便主线程可以等待工作线程

这里有一个竞争条件:如果在计算了
条件之后和执行DB操作之前立即触发shutdown标志,该怎么办

下面是一个以互斥体作为众所周知的同步原语的例子,但是有更好的方法

主线程:

int main() {
    ... wait for signal to exit the app
    // the DB operations are running on another thread
    ...
    // assume that we start shutdown here
    // also assume that there is some global mutex g_mutex
    // following line blocks if mutex is locked in worker thread:
    std::lock_guard<std::mutex> lock(g_mutex); 
    Cleanup(); //  should also ensure that worker is stopped
}
intmain(){
…等待退出应用程序的信号
//DB操作正在另一个线程上运行
...
//假设我们从这里开始关机
//还假设存在一些全局互斥g_互斥
//如果互斥锁锁定在工作线程中,则以下行块:
std::锁和保护锁(g\U互斥);
Cleanup();//还应确保工作进程已停止
}
工作线程:

void MyWorkerThread::RunMethod() 
{
  {
    std::lock_guard<std::mutex> lock(g_mutex); 
    DoSomethingOnDB();
  }
  // some other, non locked execution which doesn't prevent
  // main thread from exiting
  ...
  {
     std::lock_guard<std::mutex> lock(g_mutex);
     DoSomethingMoreOnDB();
  }
}
void MyWorkerThread::RunMethod()
{
{
std::锁和保护锁(g\U互斥);
DoSomethingOnDB();
}
//其他一些未锁定的执行不会阻止
//退出主线程
...
{
std::锁和保护锁(g\U互斥);
DoSomethingMoreOnDB();
}
}
显然,您不想重复所有的锁定,因此应该将其包装:

  void MyWorkerThread::RunMethod() 
  {
    Execute(DoSomethingOnDB);
    ...
    Execute(DoSomethingMoreOnDB);
  }

  void MyWorkerThread::Execute(DatabaseFn fn) 
  {
     std::lock_guard<std::mutex> lock(g_mutex); 
     fn();
  }
void MyWorkerThread::RunMethod()
{
执行(DoSomethingOnDB);
...
执行(DoSomethingMoreOnDB);
}
void MyWorkerThread::Execute(数据库fn)
{
std::锁和保护锁(g\U互斥);
fn();
}

您最好通过一些同步对象(事件、锁、互斥、信号量、关键部分等等)或诸如此类的对象来同步线程,以便主线程可以等待工作线程

这里有一个竞争条件:如果在计算了
条件之后和执行DB操作之前立即触发shutdown标志,该怎么办

下面是一个以互斥体作为众所周知的同步原语的例子,但是有更好的方法

主线程:

int main() {
    ... wait for signal to exit the app
    // the DB operations are running on another thread
    ...
    // assume that we start shutdown here
    // also assume that there is some global mutex g_mutex
    // following line blocks if mutex is locked in worker thread:
    std::lock_guard<std::mutex> lock(g_mutex); 
    Cleanup(); //  should also ensure that worker is stopped
}
intmain(){
…等待退出应用程序的信号
//DB操作正在另一个线程上运行
...
//假设我们从这里开始关机
//还假设存在一些全局互斥g_互斥
//如果互斥锁锁定在工作线程中,则以下行块:
std::锁和保护锁(g\U互斥);
Cleanup();//还应确保工作进程已停止
}
工作线程:

void MyWorkerThread::RunMethod() 
{
  {
    std::lock_guard<std::mutex> lock(g_mutex); 
    DoSomethingOnDB();
  }
  // some other, non locked execution which doesn't prevent
  // main thread from exiting
  ...
  {
     std::lock_guard<std::mutex> lock(g_mutex);
     DoSomethingMoreOnDB();
  }
}
void MyWorkerThread::RunMethod()
{
{
std::锁和保护锁(g\U互斥);
DoSomethingOnDB();
}
//其他一些未锁定的执行不会阻止
//退出主线程
...
{
std::锁和保护锁(g\U互斥);
DoSomethingMoreOnDB();
}
}
显然,您不想重复所有的锁定,因此应该将其包装:

  void MyWorkerThread::RunMethod() 
  {
    Execute(DoSomethingOnDB);
    ...
    Execute(DoSomethingMoreOnDB);
  }

  void MyWorkerThread::Execute(DatabaseFn fn) 
  {
     std::lock_guard<std::mutex> lock(g_mutex); 
     fn();
  }
void MyWorkerThread::RunMethod()
{
执行(DoSomethingOnDB);
...
执行(DoSomethingMoreOnDB);
}
void MyWorkerThread::Execute(数据库fn)
{
std::锁和保护锁(g\U互斥);
fn();
}

如果您的Do函数返回了一些东西,那么您可以将它们&&或| |一起返回,这样一旦其中一个返回“false”或“true”,无论哪一个是终止条件,您都将停止处理


如果在您的操作过程中(或在执行之前?)另一个线程设置了关闭标志,会发生什么情况?如果您运行了此进程,会发生什么情况。

如果Do函数返回了某个内容,那么您可以将它们放在一起,只要其中一个返回“false”或“true”,以终止条件为准,您将停止处理


如果在您的操作过程中(或在执行之前?)另一个线程设置了shutdown标志,会发生什么情况?如果您仍然运行此进程,会发生什么情况。

您的应用程序中是否已经有ReaderWriter锁?您应该将数据库关闭视为写入操作,并在启动之前获得写入程序锁。然后,在关闭过程中,其他读取操作(获取读卡器锁)或写入操作(获取写入器锁)都无法启动。

您的应用程序中是否已有读写器锁?您应该将数据库关闭视为写入操作,并在启动之前获得写入程序锁。然后,在关闭过程中,任何其他读取(获取读卡器锁)或写入操作(获取写入器锁)都无法启动。

您的标记使用将限于使用数据库的各种方法:DoSomethingOnDB、DoSomethingMoreOnDB


假设您正确地使用互斥锁和锁(这里似乎没有),您可以将与数据库相关的方法收集到一个复合方法中,并在该复合方法中锁定您的数据库,并在该复合方法中执行单个check bool操作,以便一次只有一个线程执行数据库操作。

您的标记用法将限于使用数据库的各种方法:DoSomethingOnDB、DoSomethingMoreOnDB


假设您正确地使用互斥锁和锁(这里似乎没有),您可以将与数据库相关的方法收集到一个复合方法中,并在那里锁定您的数据库,并在该方法中执行单个check bool操作,以便一次只有一个线程执行数据库操作。

代码中有一个模式,这意味着您可以将其抽象为函数:

void MyWorkerThread::execDBCommand(auto cmd){
    if  (! m_Manager->GetShutdownFlag()) {
        cmd();
    } else throw std::runtime_error("shutdown"); // or a custom exception
}
使用外部
方法,尝试在
运行方法中捕获该异常,以更直接地中断处理

但是,可能更好地将shutdown标志作为线程的信号处理,然后线程将回滚任何挂起的事务并中止