2013-04-11 3 views
0

Рабочий поток обрабатывает обновления баз. Когда приложение заканчивается, я хочу закончить его, но не позволяя ему оставить db непоследовательным. Поэтому я использую флаги перед каждой значительной операцией db. Через некоторое время, код полон, если это:Безопасное завершение потока

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

Итак, есть ли другой способ справиться выключение без спамить так много, если х?

ответ

1

Там в шаблон в коде, что означает, что вы можете абстрагировать как функция:

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

с внешним try...catch для этого исключения в вашем RunMethod более непосредственно прервать обработку.

Однако флаг отключения может быть лучше обработан как сигнал для потока, который затем откатывает любую ожидающую транзакцию и отменяет обработку без необходимости проверять состояние перед каждой инструкцией. Вам также придется маскировать сигналы во время вызовов БД.

Это зависит от вашей программной архитектуры, конечно.

2

Вам было бы лучше синхронизировать потоки через какой-либо объект синхронизации (событие, блокировку, мьютексы, семафор, критический раздел, что угодно ...) или такие, чтобы основной поток мог ждать рабочего потока.

Здесь у вас есть условие гонки: что делать, если флаг выключения поднимается сразу после if Состояние оценивается и до выполнения операции БД?

Вот иллюстрация с мьютексами как известный примитив синхронизации, но есть лучшие способы.

Главная нить:

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 
} 

Рабочий поток:

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() 
    { 
    Execute(DoSomethingOnDB); 
    ... 
    Execute(DoSomethingMoreOnDB); 
    } 

    void MyWorkerThread::Execute(DatabaseFn fn) 
    { 
    std::lock_guard<std::mutex> lock(g_mutex); 
    fn(); 
    } 
+0

но использование блокировки не будет таким же? может ли GetShutdownFlag восприниматься как единое целое? – d3r0n

+0

нет, поскольку блокировка может помешать основному потоку даже входить в выключение, если выполняется операция БД. В отличие от 'if's, блокировки блокировки блокировки, если блокировка уже выполнена (здесь здесь подразумевается примитив sync вообще). Затем только после того, как блокировка будет выпущена в другом потоке, первый поток будет продолжен. –

+0

Спасибо, это действительно хорошее решение для обработки потока db, но не отвечает на вопрос. – d3r0n

0

Если ваши функции Do возвратили что-то, то вы могли бы & & или || их вместе, поэтому, как только один из них вернет «ложный» или «истинный», в зависимости от условия завершения, вы прекратите обработку.

Что произойдет, если флаг отключения будет установлен другим потоком во время вашей операции (или непосредственно перед его выполнением?), И что произойдет, если вы все равно выполните этот процесс.

+0

Извините, но это сделает код еще менее удобочитаемым. Особенно, когда я хотел бы добавить некоторые комментарии, строки отладки и т. Д. – d3r0n

1

У вас есть замки ReaderWriter уже в ваших приложениях? Вы должны рассматривать закрытие базы данных как операцию записи и получить блокировку Writer перед ее запуском. Тогда никто из других Read (которые получают Lock Lock) или операции записи (которые получают Writer Lock) могут запускаться после завершения закрытия.

+0

К сожалению, это старый код устаревшего кода, и, насколько мне известно, он не содержит ни одного из них. И внедрение тех, кто в моем методе, будет очень похоже на текущую ситуацию. – d3r0n

0

Ваше использование флага будет ограничено различными методами, которые используют базу данных: DoSomethingOnDB, DoSomethingMoreOnDB ...

Предполагая, что вы используете семафоры и замки должным образом (который не кажется, здесь), вы можете собрать методы баз данных, связанных с в сложный метод и заблокировать вашу базу данных там и выполнить одну операцию проверки bool в этом единственном методе, чтобы только один поток выполнял операцию базы данных за раз.

+0

Я не хочу делать все изменения сразу, поэтому у меня разные методы – d3r0n

+0

, но все методы базы данных принимают меры в соответствии с m_Manager-> GetShutdownFlag(), почему бы не собрать их в одном месте? –

+0

Представьте, что всего 1 миллион пишет в 5 различных методах. Во время выполнения завершается msg. Один из методов уже завершен (он имеет 1 операцию записи: P), и это совершенно правильно для согласованности db. Но я бы не хотел дождаться конца следующего 1-го миллиарда -1, потому что я хочу закончить этот процесс немедленно. – d3r0n

Смежные вопросы