如何使用 ManualResetEvent 替换此 class 中的布尔标志

How to use ManualResetEvent to replace boolean flags in this class

我已经用相同的代码做了一个问题,并被建议使用ManualResetEvent,因为这是做我想做的事情的正确方法,我同意这一点。

问题是:我已经阅读并重新阅读了有关 ManualResetEvent 的文档和许多教程,其中包含 WaitOneSetUnsetReset 方法,但坦率地说,我不太明白应该如何使用它们。

我的代码做了什么:它一直在寻找连接的设备,当它找到一个时,它会不断验证它是否仍然连接(否则,重新开始寻找)。这是 "monitoring" activity,它可以由客户端代码使用 StartStop 方法启动或停止。还有一个 _busy 标志,因此 Stop 方法仅在一个监视周期完成后 returns。

事实是:目前 bool 标志方法不起作用,所以我想用 ManualResetEvent 方法替换它,但甚至不知道如何开始。

代码如下:

public class DeviceMonitor
{
    bool _running;
    bool _monitoring;
    bool _busy = false;
    MonitoringMode _monitoringMode;
    Thread _monitoringThread;

    readonly object _lockObj = new object();

    // CONSTRUTOR
    public DeviceMonitor()
    {
        _monitoringThread = new Thread(new ThreadStart(ExecuteMonitoring));
        _monitoringThread.IsBackground = true;
        _running = true;
        _monitoringThread.Start();            
    }


    public void Start()
    {
        _monitoring = true;
    }

    public void Stop()
    {
        _monitoring = false;
        while (_busy)
        {                
            Thread.Sleep(5);
        }
    }


    void ExecuteMonitoring()
    {
        while (_running)
        {
            Console.WriteLine("ExecuteMonitoring()");

            if (_monitoring)
            {
                lock (_lockObj)
                {
                    _busy = true;
                }
                Console.WriteLine("busy");

                if (_monitoringMode == MonitoringMode.SearchDevices)
                {
                    SearchDevices();
                }
                else
                if (_monitoringMode == MonitoringMode.MonitorDeviceConnection)
                {
                    MonitorDeviceConnection();
                }

                lock (_lockObj)
                {
                    _busy = false;
                }
                Console.WriteLine("not busy");              
            }
            Thread.Sleep(1000);
            _busy = false;                
        }
    }

    private void SearchDevices()
    {
        var connected = ListDevices();

        if (connected.Count > 0)
        {
            Device = connected.First();
            ToggleMonitoringMode();
        }
        else
            Device = null;
    }


    void MonitorDeviceConnection()
    {
        if (Device == null)
        {
            ToggleMonitoringMode();
        }
        else
        {
            bool responding = Device.isConnected;
            Console.WriteLine("responding " + responding);
            if (!responding)
            {
                Device = null;
                ToggleMonitoringMode();
            }
        }

    }


    void ToggleMonitoringMode()
    {
        if (_monitoringMode == MonitoringMode.SearchDevices)
            _monitoringMode = MonitoringMode.MonitorDeviceConnection;
        else
        if (_monitoringMode == MonitoringMode.MonitorDeviceConnection)
            _monitoringMode = MonitoringMode.SearchDevices;
    }

    enum MonitoringMode
    {
        SearchDevices,
        MonitorDeviceConnection
    }
}    

ManualResetEventAutoResetEvent 有区别。您总是必须重置 ManualResetEvents。你可以把它们想象成当你进入一个房间时手动关门和自动关门。 对于 ManualResetEvent,您必须手动调用 Reset(),否则线程将继续 运行ning,除非您调用 Reset。

我已经尝试使用 AutoResetEvent 为您的问题创建一个模拟器。希望这会给你更清晰的画面。稍后,如果您愿意,可以尝试更改和使用 ManualResetEvent

如果你看一下这个实现的想法,那么它的工作原理是这样的:

如有必要,此序列会继续切换状态。

在此示例中,我将使用 2 个线程,一个用于搜索设备,另一个用于监视设备状态。 RulyCanceler 将是您取消的标记。这将替换您使用的 _busy 标志。

public class DeviceMonitorSignaling
{
    readonly object _lockObj = new object();

    EventWaitHandle searchingHandle;
    EventWaitHandle monitoringHandle;

    bool _running;
    bool _monitoring;
    volatile Device device;

    MonitoringMode _monitoringMode;

    Thread _monitoringThread;
    Thread _searchDeviceThread;
    RulyCanceler CancelToken;

    // CONSTRUTOR
    public DeviceMonitorSignaling()
    {
        CancelToken = new RulyCanceler();

        searchingHandle = new AutoResetEvent(false);
        monitoringHandle = new AutoResetEvent(false);

        _monitoringThread = new Thread
            (() =>
            {
                try { MonitorDeviceConnection(CancelToken); }
                catch (OperationCanceledException)
                {
                    Console.WriteLine("Canceled Search!");
                }
            });

        _searchDeviceThread = new Thread(() =>
        {
            try { SearchDevices(CancelToken); }
            catch (OperationCanceledException)
            {
                Console.WriteLine("Canceled Monitor!");
            }
        });

        _monitoringThread.IsBackground = true;
    }


    public void Start()
    {
        _monitoring = true;
        _running = true;

        _searchDeviceThread.Start();
        _monitoringThread.Start();
    }

    public void Stop()
    {
        CancelToken.Cancel();

        // One of the thread would be sleeping to identify and recall it.
        WakeSleepingThread();
    }

    /// <summary>
    /// Method to search the device.
    /// </summary>
    /// <param name="cancelToken"></param>
    void SearchDevices(RulyCanceler cancelToken)
    {
        while (_running)
        {
            cancelToken.ThrowIfCancellationRequested();
            Console.WriteLine("Searching devices....");
            Thread.Sleep(1000);
            device = new Device(); // may be some logic to detect the device.

            Console.WriteLine("Finished!!! Searching devices. Start monitoring.");

            if(device != null)
            {
                // Block the search thread and start the monitoring thread.
                ToggleMonitoringMode(); 
            }
        }
    }

    /// <summary>
    /// Once the device is detected 
    /// </summary>
    /// <param name="cancelToken"></param>
    void MonitorDeviceConnection(RulyCanceler cancelToken)
    {
        monitoringHandle.WaitOne();
        Console.WriteLine("monitoring started.");

        while (_monitoring)
        {
            cancelToken.ThrowIfCancellationRequested();
            Thread.Sleep(1000);

            if (device == null)
            {
                Console.WriteLine("Disconnected Invoking search.");
                // Block monitoring thread and awake the device search.
                ToggleMonitoringMode();
            }
            else
            {
                bool responding = device.isConnected;
                Console.WriteLine("responding {0}", responding);
                if (!responding)
                {
                    Console.WriteLine("Not responding. Invoking search.");
                    device = null;
                    // Block monitoring thread and awake the device search.
                    ToggleMonitoringMode();
                }
            }
        }
    }


    internal void ToggleMonitoringMode()
    {
        if (_monitoringMode == MonitoringMode.SearchDevices)
        {
            _monitoringMode = MonitoringMode.MonitorDeviceConnection;
            monitoringHandle.Set();
            searchingHandle.WaitOne();
        }
        else if (_monitoringMode == MonitoringMode.MonitorDeviceConnection)
        {
            _monitoringMode = MonitoringMode.SearchDevices;
            searchingHandle.Set();
            monitoringHandle.WaitOne();
        }
    }

    internal void WakeSleepingThread()
    {
        if(_monitoringMode == MonitoringMode.MonitorDeviceConnection)
        {
            searchingHandle.Set();
        }
        else
        {
            monitoringHandle.Set();
        }
    }

    enum MonitoringMode
    {
        SearchDevices,
        MonitorDeviceConnection
    }

    /// <summary>
    /// For test purpose remove the device.
    /// </summary>
    internal void DisconnectDevice()
    {
        if(device != null)
        {
            device = null;
        }
    }

    /// <summary>
    /// For test purpose change the device status
    /// </summary>
    internal void ChangeDeviceState()
    {
        if (device != null)
        {
            device.Disconnect();
        }
    }

    /// <summary>
    /// Dummy device
    /// </summary>
    internal class Device
    {
        public bool isConnected = false;

        public Device()
        {
            isConnected = true;
        }

        public void Disconnect()
        {
            isConnected = false;
        }
    }

    internal class RulyCanceler
    {
        object _cancelLocker = new object();
        bool _cancelRequest;
        public bool IsCancellationRequested
        {
            get { lock (_cancelLocker) return _cancelRequest; }
        }
        public void Cancel() { lock (_cancelLocker) _cancelRequest = true; }
        public void ThrowIfCancellationRequested()
        {
            if (IsCancellationRequested) throw new OperationCanceledException();
        }
    }
}

如果您查看 Stop() 方法,此方法使用 CancelToken 发送中断信号。 WakeSleepThread 用于唤醒两个休眠线程中的任何一个。

static void Main(string[] args)
        {
            var obj = new DeviceMonitorSignaling();
            Console.WriteLine("Starting...");
            obj.Start();

            Thread.Sleep(4000); // after 4 sec remove the device.

            Console.WriteLine("Changing device state.");
            obj.DisconnectDevice();

            Thread.Sleep(4000); // // after 4 sec change the device status.
            obj.ChangeDeviceState();

            Console.Read();
            Console.WriteLine("Stopping...");

            obj.Stop();
            Console.Read();
        }

我已经使用上面的模拟来更改设备状态和设备对象以设置为空。如果您 运行 该程序,您将看到类似这样的输出。

注意:此示例代码可能有待改进的地方。随意优化。

参考文献 - http://www.albahari.com/threading/