问题描述:

Worker thread handles database updates. When application ends I want to terminate it but without letting it to leave db inconsistent. So I use flags before each significant db operation. After a while, code is full of if's :

 void MyWorkerThread::RunMethod(){

if (false == m_Manager->GetShutdownFlag()) {

DoSomethingOnDB();

}

if (false == m_Manager->GetShutdownFlag()) {

DoSomethingMoreOnDB();

}

...

So, is there any other way to handle shutdown without spamming so many if's?

网友答案:

There's a pattern in your code, which means that you can abstract it as a function:

void MyWorkerThread::execDBCommand(auto cmd){
    if  (! m_Manager->GetShutdownFlag()) {
        cmd();
    } else throw std::runtime_error("shutdown"); // or a custom exception
}

with an outer try...catch for that exception in your RunMethod to more directly interrupt processing.

However, the shutdown flag could perhaps be better handled as a signal for the thread, which would then rollback any pending transaction and abort processing, without having to check the state before each instruction. You would also have to mask signals during DB calls.

It depends on your programme architecture of course.

网友答案:

You would be better off synchronizing the threads via some synchronization object (event,lock, mutex, semaphore, critical section, whatever...) or such, so that main thread could wait for worker thread.

Here you have a race condition: what if shutdown flag gets raised immediately after if condition is evaluated and before DB operation is executed?

Here's an illustration with mutexes as a well known synchronization primitive, but there are better ways.

Main thread:

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
}

The worker thread:

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();
  }
}

as obviously you don't want to repeat all the locking, you should wrap it:

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

  void MyWorkerThread::Execute(DatabaseFn fn) 
  {
     std::lock_guard<std::mutex> lock(g_mutex); 
     fn();
  }
网友答案:

Do you have ReaderWriter Locks already in your applications? You should treat Database closing as a Write Operation and get a Writer Lock before starting it. Then none of the other Read(which get a Reader Lock) or Write operations(which get a Writer Lock) can start once the closing is in progress.

网友答案:

If your Do functions returned something you could then && or || them together so as soon as one of them returns "false" or "true" whichever is the terminating condition, you would stop processing.

What happens if the shutdown flag gets set by a different thread during your operation (or just before executing it?) and what would happen if you ran this process anyway.

网友答案:

Your flag usage will be limited to the variety of methods which use database: DoSomethingOnDB, DoSomethingMoreOnDB ...

Assuming you use mutexes and locks properly (which does not seem here), you can gather database related methods into a compound method and lock your database there and do a single check bool operation in that single method so that only a single thread make a database operation at a time.

相关阅读:
Top