C# 定期异步运行C中线程不安全的函数#

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

我有一个IP摄像头,附带有它自己的图书馆。 它的方法都是同步的,它们看起来是这样的

这是为了不存在竞争条件,即当Configure()运行时,我不能调用GetImage()。 请注意,其中一些方法需要几秒钟才能完成

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"));
    }
}