如何使用 ManualResetEvent 替换此 class 中的布尔标志
How to use ManualResetEvent to replace boolean flags in this class
我已经用相同的代码做了一个问题,并被建议使用ManualResetEvent
,因为这是做我想做的事情的正确方法,我同意这一点。
问题是:我已经阅读并重新阅读了有关 ManualResetEvent
的文档和许多教程,其中包含 WaitOne
、Set
、Unset
和 Reset
方法,但坦率地说,我不太明白应该如何使用它们。
我的代码做了什么:它一直在寻找连接的设备,当它找到一个时,它会不断验证它是否仍然连接(否则,重新开始寻找)。这是 "monitoring" activity,它可以由客户端代码使用 Start
和 Stop
方法启动或停止。还有一个 _busy
标志,因此 Stop
方法仅在一个监视周期完成后 returns。
事实是:目前 bool 标志方法不起作用,所以我想用 ManualResetEvent
方法替换它,但甚至不知道如何开始。
- 我应该用 ManualResetEvents 一对一地替换标志吗?
SearchDevices()
和 MonitorDeviceConnection()
方法是否应该有条件地 运行 在同一个线程中,或者每个方法都应该有(或是)自己的线程?
Start
和 Stop
(打开和关闭,从客户端代码调用)之间的区别以及两种监视方法之间的 "selecting" 如何影响每个 ManualResetEvent 的使用方式? (不太确定这个问题是否有意义)
- 使用枚举标志 select 两种可能的执行路径之一是一种代码味道,不是吗?在“
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
}
}
ManualResetEvent
和 AutoResetEvent
有区别。您总是必须重置 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();
}
我已经使用上面的模拟来更改设备状态和设备对象以设置为空。如果您 运行 该程序,您将看到类似这样的输出。
注意:此示例代码可能有待改进的地方。随意优化。
我已经用相同的代码做了一个问题,并被建议使用ManualResetEvent
,因为这是做我想做的事情的正确方法,我同意这一点。
问题是:我已经阅读并重新阅读了有关 ManualResetEvent
的文档和许多教程,其中包含 WaitOne
、Set
、Unset
和 Reset
方法,但坦率地说,我不太明白应该如何使用它们。
我的代码做了什么:它一直在寻找连接的设备,当它找到一个时,它会不断验证它是否仍然连接(否则,重新开始寻找)。这是 "monitoring" activity,它可以由客户端代码使用 Start
和 Stop
方法启动或停止。还有一个 _busy
标志,因此 Stop
方法仅在一个监视周期完成后 returns。
事实是:目前 bool 标志方法不起作用,所以我想用 ManualResetEvent
方法替换它,但甚至不知道如何开始。
- 我应该用 ManualResetEvents 一对一地替换标志吗?
SearchDevices()
和MonitorDeviceConnection()
方法是否应该有条件地 运行 在同一个线程中,或者每个方法都应该有(或是)自己的线程?Start
和Stop
(打开和关闭,从客户端代码调用)之间的区别以及两种监视方法之间的 "selecting" 如何影响每个 ManualResetEvent 的使用方式? (不太确定这个问题是否有意义)- 使用枚举标志 select 两种可能的执行路径之一是一种代码味道,不是吗?在“
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
}
}
ManualResetEvent
和 AutoResetEvent
有区别。您总是必须重置 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();
}
我已经使用上面的模拟来更改设备状态和设备对象以设置为空。如果您 运行 该程序,您将看到类似这样的输出。
注意:此示例代码可能有待改进的地方。随意优化。