0

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

问题是:我已经阅读并重新阅读了有关ManualResetEventWaitOne、和方法的文档和许多教程Set,但坦率地说,我不太了解它们应该如何使用。UnsetReset

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

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

  • 我应该一对一地用 ManualResetEvents 替换标志吗?
  • 应该SearchDevices()MonitorDeviceConnection()方法有条件地在同一个线程中运行,还是应该每个都有(或是)自己的线程?
  • StartStop(打开和关闭,从客户端代码调用)和两种监视方法之间的“选择”之间的区别如何影响每个 ManualResetEvent 的使用方式?(不太确定这个问题是否有意义)
  • 使用枚举标志来选择两种可能的执行路径之一是一种代码味道,不是吗?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
    }
}    
4

1 回答 1

1

ManualResetEvent和有区别AutoResetEvent。您总是必须重置ManualResetEvents. 当您进入房间时,您可以将它们想象为手动关门和自动关门。因为ManualResetEvent您必须手动调用,Reset()否则线程将继续运行,除非您调用重置。

我尝试使用AutoResetEvent. 希望这会给你更清晰的画面。稍后,如果您愿意,可以尝试更改和使用ManualResetEvent

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

如果需要,此序列继续进行状态切换。

在这个示例中,我将使用 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();
        }

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

线程模拟的工作流程

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

参考资料 - http://www.albahari.com/threading/

于 2015-09-03T18:18:51.193 回答