C: шаблон для возврата асинхронной ошибки из фонового потока? - PullRequest
1 голос
/ 13 марта 2009

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

Моя проблема в том, что я еще не нашел элегантный способ вернуть ошибки из фонового потока. Предположим, что фоновый поток реорганизует файл или выполняет периодическое обслуживание, и он терпит неудачу - что делать?

В настоящее время я вижу два варианта:

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

Мне не нравится эта опция - пользователь даже не знает, что существует фоновый поток, поэтому он, скорее всего, забудет о настройке функции обратного вызова. С точки зрения удобства использования эта опция плохая.

2) фоновый поток сохраняет ошибку в глобальной переменной, и следующая функция API возвращает эту ошибку.

Это то, что я сейчас делаю, но я также не на 100% доволен этим, потому что это означает, что пользователи должны ожидать КАЖДЫЙ возможный код ошибки, возвращаемый из каждой функции API. То есть если фоновый поток устанавливает ошибку ввода-вывода, а пользователь просто хочет узнать версию библиотеки, он получит ошибку ввода-вывода, хотя API-вызов get_version () вообще не обращается к диску. Опять же, плохое удобство использования ...

Любые другие предложения / идеи?

Ответы [ 7 ]

3 голосов
/ 13 марта 2009

Возможно, для "длительных операций" (для которых вы хотите использовать поток) предоставьте пользователям два варианта:

  • блокировка DoAction(...), возвращающая статус
  • неблокирующая DoActionAsync(..., <callback>), которая дает статус предоставленной пользователем функции обратного вызова

Это дает пользователю выбор того, как он хочет обрабатывать длинную операцию (вместо того, чтобы вы решали за него), и ясно, как будет возвращен статус.

Примечание: я полагаю, что если они вызывают DoActionAsync, а пользователь не указывает обратный вызов (например, они передают null), то вызов не будет блокироваться, но у пользователя не будет / не нужно обрабатывать статус.

1 голос
/ 16 марта 2009

Вы должны хранить потокобезопасный список (или очередь) событий и предупреждений об ошибках. Рабочий поток может отправлять события в список, затем основной поток может читать события из списка, по одному за раз или в пакете, чтобы предотвратить состояние гонки. В идеале основной поток должен получить копию очереди событий и очистить ее, чтобы не было изменений дублирующих событий в случае нескольких основных или рабочих потоков. События в списке будут иметь тип и подробности.

1 голос
/ 13 марта 2009

Мне интересно знать, как статус завершения сообщается вызывающей стороне API.

Так как фоновый поток выполняет все выполнение. Либо поток переднего плана выбирает ожидание до завершения, как синхронный. Или поток переднего плана может выполнять другие задачи, регистрируясь для обратного вызова.

Теперь, поскольку первый метод является синхронным, как и использование вами глобальной переменной. Вы можете использовать очередь сообщений с 1 участником вместо вашей глобальной переменной. Сейчас, - Абонент может либо опросить очередь сообщений на предмет статуса - Звонящий может заблокировать ожидание в очереди сообщений для статуса

Что я могу придумать,

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

0 голосов
/ 24 апреля 2013

Я согласен с Шоном. Очередь сообщений с циклом событий.

В случае ошибки фоновый поток может быть вставлен в очередь. Цикл событий будет блокироваться, пока не станет доступно новое сообщение.

Я с большим успехом использовал Apache Portable Runtime для создания телекоммуникационных серверов с высокой скоростью транзакций. Это никогда не подводило.

Я использую 1 поток для вставки в очередь, это будет ваш фоновый поток. Цикл событий будет выполняться в другом потоке и блокироваться, пока не будет вставлено новое сообщение.

Я бы порекомендовал пул потоков APR с очередью APR FIFO (которая также является поточно-ориентированной).

Быстрый дизайн здесь:

void background_job()
{
    /* There has been an error insert into the queue */
    apr_status_t rv = 0;

    rv = apr_queue_push(queue, data);
    if(rv == APR_EOF) {
        MODULE_LOG(APK_PRIO_WARNING, "Message queue has been terminated");
        return FALSE;
    }
    else if(rv == APR_EINTR) {
        MODULE_LOG(APK_PRIO_WARNING, "Message queue was interrupted");
        return FALSE;
    }
    else if(rv != APR_SUCCESS) {
        char err_buf[BUFFER_SIZE];
        MODULE_LOG(APK_PRIO_CRITICAL, "Failed to push to queue %s", apr_strerror(rv, err_buf, BUFFER_SIZE));

        return FALSE;
    }

    return TRUE;   
}

void evt_loop()
{
   while(continue_loop) {
    apr_status_t rv = 0;

    rv = apr_queue_pop(queue, data);
    if(rv == APR_EOF) {
        MODULE_LOG(APK_PRIO_WARNING, "Message queue has been terminated");
        return FALSE;
    }
    else if(rv == APR_EINTR) {
        MODULE_LOG(APK_PRIO_WARNING, "Message queue was interrupted");
        return FALSE;
    }
    else if(rv != APR_SUCCESS) {
        char err_buf[BUFFER_SIZE];
        MODULE_LOG(APK_PRIO_CRITICAL, "Failed to pop from the queue %s", apr_strerror(rv, err_buf, BUFFER_SIZE));

        return FALSE;
    }

    return TRUE;
   } 
}

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

Надеюсь, это поможет

0 голосов
/ 30 марта 2009

Вы могли бы взглянуть на Future API java для альтернативного механизма для обработки асинхронных вызовов и ошибок. Вы можете легко заменить проверенные исключения некоторыми методами isError () или getError (), если хотите.

0 голосов
/ 20 марта 2009

Спасибо за все хорошие ответы. Вы предоставили мне много материала для размышления.

Некоторые из вас предложили обратные вызовы. Сначала я думал, что обратный вызов - хорошая идея. Но это просто переносит проблему на пользователя. Если пользователь получает уведомление об асинхронной ошибке, как он будет с этим бороться? ему придется прервать и / или уведомить о своем синхронном потоке программ, что, как правило, сложно и часто нарушает дизайн.

Решение, которое я делаю сейчас: если фоновый поток генерирует ошибку, следующий вызов API вернет ошибку BACKGROUND_ERROR_PENDING. С помощью отдельной функции API (get_background_error ()) пользователь может посмотреть на этот код ошибки, если он заинтересован в нем.

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

0 голосов
/ 13 марта 2009

Если вы предоставляете библиотеку и пытаетесь скрыть дорогую работу через поток, я бы посоветовал не делать это таким образом. Если что-то дорогое, это должно быть видно вызывающему абоненту, и если это его раздражает, он должен сам позаботиться о фоновом режиме / потоке. Таким образом, он также имеет полный контроль над ошибкой. Это также отнимает контроль над его процессом у разработчика, который использует вашу библиотеку.

Если вы все еще хотите использовать потоки, я бы посоветовал действительно следовать маршруту обратного вызова, но четко указать в API и документации, что при выполнении этой задачи будет выполняться фоновый поток, и поэтому обратный вызов необходим.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...