C# 定期异步运行C中线程不安全的函数#
我有一个IP摄像头,附带有它自己的图书馆。 它的方法都是同步的,它们看起来是这样的 这是为了不存在竞争条件,即当Configure()运行时,我不能调用GetImage()。 请注意,其中一些方法需要几秒钟才能完成C# 定期异步运行C中线程不安全的函数#,c#,asynchronous,thread-safety,C#,Asynchronous,Thread Safety,我有一个IP摄像头,附带有它自己的图书馆。 它的方法都是同步的,它们看起来是这样的 这是为了不存在竞争条件,即当Configure()运行时,我不能调用GetImage()。 请注意,其中一些方法需要几秒钟才能完成 public interface ICamera { bool Configure(object args); // Takes a long time Image GetImage(); // Takes a little time but must not be
public interface ICamera
{
bool Configure(object args); // Takes a long time
Image GetImage(); // Takes a little time but must not be accessed by other thread.
}
现在,我想将这个库包装在一个服务类中,该服务类将在新框架准备就绪时启动一个事件。
事件的订阅者将负责更新ui
我想做一些类似(伪c#)
我如何确保不管调用线程是什么,所有ICamera的方法都在单个后台线程上顺序运行
我走对了吗?我不明白我是否应该使用任务,锁定每个摄像头访问权限(但为每个帧锁定一个对象似乎非常昂贵)。
解决这个我认为并不罕见的问题最实际的方法是什么
我还有这些问题:
在哪个线程上运行lambda(它在调用方的线程上吗?每次调用都在不同的线程上吗?我希望它在一个线程中。)
我想了解如何构造ICameraService,使所有线程不安全的代码都没有竞争条件,但仍然提供异步接口(使用事件或异步/等待)调用CameraService的ViewModel。NET Timer类具有一项功能,可提供锁定特定对象的定期事件。看 在线程池中使用自己的锁是一种语法上的优势。它不会保证每个间隔事件都在同一个线程上运行,但会保证每次处理一个事件
如果您想要更好的控制或保证在单个线程上运行,我会确定设置下一个线程超时所用的时间。只需启动一个线程,初始化摄像头并定期运行捕获,直到调用Stop camera。别忘了,NewFrameAvailable在捕获线程上运行,所以要么创建一个副本,要么根据处理所用的时间计算WaitOne(超时) 你可以这样做:
public class ICameraService
{
public event EventHandler NewFrameAvailable;
public event EventHandler StateChanged;
private ICamera camera;
private Timer frameTimer;
private ManualResetEvent _terminate = new AutoResetEvent(false);
private int interval;
private Thread thread;
public void StartCapture(int interval)
{
_terminate.Reset();
this.interval = interval;
thread = new Thread(ThreadMethod);
}
public void ThreadMethod()
{
StateChanged?.Invoke(this, StateChangedEventArgs("Configuring"));
camera.Configure(...);
StateChanged?.Invoke(this, StateChangedEventArgs("Capturing"));
while(!_terminate.WaitOne(interval))
{
var image = camera.Capture();
image = DoSomeBasicProcessing(image);
NewFrameAvailable?.Invoke(this, NewFrameAvailableEventArgs(image));
}
StateChanged?.Invoke(this, StateChangedEventArgs("Idle"));
}
public void StopCapture()
{
_terminate.Set();
thread.Join();
StateChanged?.Invoke(this, StateChangedEventArgs("Idle"));
}
}
下面是同一主题的一个变体,使用TPL类而不是线程原语:
public class CameraService
{
public event EventHandler NewFrameAvailable;
public event EventHandler StateChanged;
private ICamera _camera;
private CancellationTokenSource _cts;
private Task _task;
public void StartCapture(TimeSpan interval)
{
_cts = new CancellationTokenSource();
_task = Task.Factory.StartNew(() =>
{
StateChanged?.Invoke(this, new StateChangedEventArgs("Configuring"));
_camera.Configure(/* ... */);
StateChanged?.Invoke(this, new StateChangedEventArgs("Capturing"));
while (true)
{
var delayTask = Task.Delay(interval, _cts.Token);
var image = _camera.Capture();
image = DoSomeBasicProcessing(image);
NewFrameAvailable?.Invoke(this, new NewFrameEventArgs(image));
try { delayTask.GetAwaiter().GetResult(); }
catch (OperationCanceledException) { break; }
}
StateChanged?.Invoke(this, new StateChangedEventArgs("Idle"));
}, _cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
public void StopCapture()
{
_cts.Cancel();
try { _task.GetAwaiter().GetResult(); }
catch (OperationCanceledException) { } // Ignore
_cts.Dispose();
StateChanged?.Invoke(this, StateChangedEventArgs("Idle"));
}
}
<>如果您希望在UI线程上(可选地)提高代码< > CaseService Ungs/Cux>类的事件,您可以考虑模仿<代码> Stase.Time.Time< /Cord>类的属性。您可以研究此类的源代码,以了解如何使用此属性。我是否正确理解它,您希望在后台线程上运行
camera.Configure()
和camera.Capture()?定义“所有方法”正确,我将进行编辑以使其更清晰,主要问题是,在调用camera.Capture()
之前,如何执行Configure()
方法(并且不要阻止调用线程。谢谢,更接近解决方案。有没有办法让计时器滴答作响而不是一个while(true)循环?或者我可以在while结束时执行ThreadSleep()。这就是_terminate事件的全部技巧。它会阻止当前线程(就像睡眠),但是当\u terminate.Set()设置;
后,它将直接返回true,然后退出循环。我喜欢使用Tasks的解决方案,但我相信StateChanged?.Invoke(这是新的StateChangedEventArgs(“Idle”);
行在while(true){
从未被调用。@AFusco是的,你是对的。我把try/catch放错地方了。现在应该可以了。
public class CameraService
{
public event EventHandler NewFrameAvailable;
public event EventHandler StateChanged;
private ICamera _camera;
private CancellationTokenSource _cts;
private Task _task;
public void StartCapture(TimeSpan interval)
{
_cts = new CancellationTokenSource();
_task = Task.Factory.StartNew(() =>
{
StateChanged?.Invoke(this, new StateChangedEventArgs("Configuring"));
_camera.Configure(/* ... */);
StateChanged?.Invoke(this, new StateChangedEventArgs("Capturing"));
while (true)
{
var delayTask = Task.Delay(interval, _cts.Token);
var image = _camera.Capture();
image = DoSomeBasicProcessing(image);
NewFrameAvailable?.Invoke(this, new NewFrameEventArgs(image));
try { delayTask.GetAwaiter().GetResult(); }
catch (OperationCanceledException) { break; }
}
StateChanged?.Invoke(this, new StateChangedEventArgs("Idle"));
}, _cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
public void StopCapture()
{
_cts.Cancel();
try { _task.GetAwaiter().GetResult(); }
catch (OperationCanceledException) { } // Ignore
_cts.Dispose();
StateChanged?.Invoke(this, StateChangedEventArgs("Idle"));
}
}