Правильный подход к временной приостановке рабочего потока

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

Любые дополнительные рекомендации по этому поводу, о которых я должен знать?

Спасибо!

  • это на С#/.NET4

person AlexVPerl    schedule 06.04.2012    source источник
comment
Спасибо, что поймали это! Я обновил пост.   -  person AlexVPerl    schedule 06.04.2012
comment
Что решает, когда поток должен стать активным?   -  person Martin James    schedule 06.04.2012
comment
Пользовательский ввод, например нажатие кнопки или щелчок мыши   -  person AlexVPerl    schedule 06.04.2012


Ответы (3)


Просто используйте событие, чтобы приостановить рабочий поток: reset — приостановлено, set — не приостановлено (рабочее) состояние.

Вот черновик кода, демонстрирующий этот подход.

class Worker
{
    private Thread _thread;

    // Un-paused by default.
    private ManualResetEvent _notToBePaused = new ManualResetEvent(true);

    public Worker()
    {
        _thread = new Thread(Run)
            {
                IsBackground = true
            };
    }

    /// <summary>
    /// Thread function.
    /// </summary>
    private void Run()
    {
        while (true)
        {
            // Would block if paused!
            _notToBePaused.WaitOne();

            // Process some stuff here.
        }
    }

    public void Start()
    {
        _thread.Start();
    }

    public void Pause()
    {
        _notToBePaused.Reset();
    }

    public void UnPause()
    {
        _notToBePaused.Set();
    }
}
person Sergey Vyacheslavovich Brunov    schedule 06.04.2012

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

ThreadPool.QueueUserWorkItem(() => {
    // My temporary work here
});

Если вы настаиваете на постоянном рабочем потоке, запустите его:

// This is our latch- we can use this to "let the thread out of the gate"
AutoResetEvent threadLatch = new AutoResetEvent(false);

// The thread runs this
public void DoBackgroundWork() {
    // Making sure that the thread is a background thread
    // ensures that the endless loop below doesn't prevent
    // the program from exiting
    Thread.IsBackground = true;
    while (true) {

        // The worker thread will get here and then block 
        // until someone Set()s the latch:
        threadLatch.WaitOne();

        // Do your work here
    }
}

//  To signal the thread to start:
threadLatch.Set();

Также обратите внимание, что если этот фоновый поток вообще будет взаимодействовать с пользовательским интерфейсом, вам потребуется Invoke или BeginInvoke соответственно. См. http://weblogs.asp.net/justin_rogers/pages/126345.aspx

person Chris Shain    schedule 06.04.2012
comment
Знаете ли вы, будет ли QueueUserWorkItem выполняться в одном новом потоке или в нескольких? Это означает, что если вы вызываете дважды, добавляя 2 отдельных рабочих элемента, есть ли гарантия, что оба они будут выполняться последовательно или при вызове мы полностью отказываемся от управления, и может быть создано несколько новых потоков? - person AlexVPerl; 07.04.2012
comment
Последний. У вас будет несколько потоков, если вы не синхронизируете их работу вручную. - person Chris Shain; 07.04.2012

Сигнализация с помощью WaitHandle — правильный путь, но просто чтобы добавить к тому, что уже сказали другие.

Я обычно использовал 2 сигнала, работающих вместе, иначе вы не знали бы, следует ли «продолжить» или «выйти», когда это необходимо, или пришлось бы прибегать к менее изящному способу сделать это (остановка потока - конечно, есть есть и другие способы сделать что-то подобное, только один «шаблон»). Так что обычно это работает с сигналом «выход» и сигналом «доступна новая работа» — работая в унисон. например

WaitHandle[] eventArray = new WaitHandle[2] { _exitEvent, _newWorkEvent };
while ((waitid = WaitHandle.WaitAny(eventArray, timeout, false)) > 1)
{
    // do your work, and optionally handle timeout etc.
}

примечание:
выход – это ManualResetEvent с начальным состоянием "false" – событие "Set" для выхода.
_newWork – это либо Manual, и в этом случае вам нужно приостановить/продолжить извне, чего вы и хотели, я думаю -
... или также может быть new AutoResetEvent(false), который вы «сигнализируете» для выполнения одного цикла работы, сигнал сразу же возвращается к «ложному» — и вам нужно повторять это для каждой «новой партии» работы — это немного упрощено . (часто это идет рука об руку с передачей некоторых «сообщений», конечно, каким-то образом синхронизированных).

Надеюсь, это добавит больше информации,

person NSGaga-mostly-inactive    schedule 06.04.2012