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

class Program
{
    private Thread workerThread;
    private AutoResetEvent aResetEvent;
    private bool _continueProcessing;
    private bool active;
    private Object locker = new Object();

    public Program()
    {
        workerThread = new Thread(DoSomeProcessing);
        workerThread.IsBackground = true;
        aResetEvent = new AutoResetEvent(false);
    }

    public bool ContinueProcessing
    {
        get
        {
            lock (locker)
            {
                return _continueProcessing;
            }
        }
        set 
        {
            if (value)
            {
                aResetEvent.Set();
            }
            else
            {

                aResetEvent.Reset();
            }
            lock (locker)
            {
                _continueProcessing = value;
            }
        }
    }

    public void DoSomeProcessing()
    {
        int i = 0;
        try
        {
            while (active)
            {
                aResetEvent.WaitOne();
                // do some work and sleep
                lock (locker)
                {
                    if (ContinueProcessing)
                    {
                        aResetEvent.Set();
                    }
                }
            }
        }
        catch(ThreadInterruptedException tie)
        {
            Console.WriteLine("Shutting down.");
        }
        // any shutdown processing            
    }

    public void StopProcessing()
    {
        workerThread.Interrupt();
        workerThread.Join();
    }

    public void PauseProcessing()
    {
        ContinueProcessing = false;
    }

    public void Continue()
    {
        ContinueProcessing = true;
    }

    public void StartProcessing()
    {
        ContinueProcessing = true;
        active = true;
    }
}

РЕДАКТИРОВАТЬ: Привет снова. Я воспользовался отзывами и гораздо больше доволен своей реализацией. Я хотел бы добавить одну небольшую вещь: когда я делаю паузу, я хотел бы подождать, чтобы убедиться, что поток приостановлен и больше не выполняет работу. Это возможно? Может быть, мне просто заменить паузу и возобновить только start и stop, а затем на остановке выполнить thred.join (). Комментарии?

2
uriDium 12 Фев 2010 в 15:22
1
Интересно, возникнут ли у вас проблемы с заказом в установщике ContinueProcessing?
 – 
ziya
12 Фев 2010 в 15:36
Вы устанавливаете _continueProcessing дважды - заблокирован и разблокирован
 – 
tanascius
12 Фев 2010 в 15:37
Может быть, вам стоит поставить блокировку вокруг всех (в установщике ContinueProcessing) или подумать о том, как не использовать флаг? dotnet.org.za/ markn / archive / 2008/10/21 /…
 – 
ziya
12 Фев 2010 в 15:44

3 ответа

Лучший ответ

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

 class Program {
        static void Main(string[] args) {

            //NOTE: if worker goes out of scope it will be collected -> ex: promote to field in real use
            Worker worker = new Worker();
            System.Threading.Thread workerThread = new System.Threading.Thread(new System.Threading.ThreadStart(worker.DoWork));
            workerThread.IsBackground = true;
            workerThread.Start();

            // test 
            worker.Resume();
            System.Threading.Thread.Sleep(2000);

            worker.Pause();
            System.Threading.Thread.Sleep(2000);

            worker.Resume();
            System.Threading.Thread.Sleep(2000);

            worker.Exit();
            System.Threading.Thread.Sleep(5000);        
        }
    }

    public class Worker {

        private readonly System.Threading.ManualResetEvent _Gate;
        private bool _IsActive;

        public Worker() {

            _Gate = new System.Threading.ManualResetEvent(false);
            _IsActive = true;
        }

        public void DoWork() {

            while (IsActive) {
                _Gate.WaitOne();
                // do work

                // can yield the thread 
                System.Threading.Thread.Sleep(1);
            }

            // dispose
            _Gate.Close();
        }

        private bool IsActive {
            get {
                lock (_Gate) {
                    return _IsActive;
                }
            }
        }

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

        public void Resume() {
            _Gate.Set();
        }

        public void Exit() {
            lock (_Gate) {
                _IsActive = false;
            }
        }
    }
1
MaLio 12 Фев 2010 в 16:35

Выглядит слишком сложным

А также

 public void StopProcessing() 
    { 
        workerThread.Interrupt(); 
        workerThread.Join(); 
    } 

Можно удалить, если вы просто позволите потоку выйти из метода

0
MaLio 12 Фев 2010 в 15:41

Если вы перешли на использование ManualResetEvent, вы можете удалить переменную _continueProcessing. В наборе просто вызовите Set или Reset по событию. В получателе вы можете вернуть aResetEvent.WaitOne (0). Затем вы можете удалить фрагмент кода в конце DoSomeProcessing, который устанавливает событие, если обработка должна продолжаться. Кроме того, поскольку ManualResetEvent сам по себе потокобезопасен, вы можете полностью снять блокировку.

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

В качестве альтернативы вы можете использовать другое событие, чтобы сигнализировать о том, что цикл должен выйти, и изменить время ожидания, чтобы использовать WaitHandle.WaitAny ().

0
pipTheGeek 12 Фев 2010 в 16:03