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

时间:2015-09-02 18:17:18

标签: c# multithreading manualresetevent waithandle

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

问题是:我已阅读并重新阅读了有关ManualResetEventWaitOneSet和{{1}的Unset的文档和大量教程方法,但坦率地说,我不太明白它们应该如何使用。

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

事实是:目前bool标志方法不起作用,所以我想用Stop方法替换它,但是无法弄清楚如何开始。

  • 我应该通过ManualResetEvents一对一替换标志吗?
  • ManualResetEventSearchDevices()方法是否有条件地在同一个线程中运行,或者每个方法是否应该拥有(或者是)自己的线程?
  • MonitorDeviceConnection()Start之间的差异(打开和关闭,从客户端代码调用)和两种监控方法之间的“选择”如何影响每个ManualResetEvent的使用方式? (不太确定这个问题有多大意义)
  • 使用枚举标志选择两种可能的执行路径之一是代码味道,不是吗?在“Stop上下文”中摆脱它的合理方法是什么?

以下是代码:

ManualResetEvent

1 个答案:

答案 0 :(得分:1)

ManualResetEventAutoResetEvent之间存在差异。您始终必须重置ManualResetEvents。当你进入一个房间时,你可以把它们想象成手动门关闭和自动关门。 对于ManualResetEvent,您必须手动调用Reset(),否则线程将继续运行,除非您调用重置。

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

如果你看看这个实现的想法,那么它的工作原理如下: Thread working with 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();
        }

我使用上面的模拟来更改设备状态,将设备对象设置为null。如果你运行该程序,你会看到类似的输出。

Workflow of thread simulations

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

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