C#-为高度并发的方法设置CancellationToken的正确方法
我有一个函数,每次对象引发事件时都刷新UI。但是,任何单个对象都可以在1秒内引发多个事件,并且集合中可能有10000多个对象在这样做。我的想法是捕获最后一个事件,并在间隔1秒后丢弃任何挂起的事件 每当任何对象引发任何事件时,都会调用以下C#-为高度并发的方法设置CancellationToken的正确方法,c#,task,C#,Task,我有一个函数,每次对象引发事件时都刷新UI。但是,任何单个对象都可以在1秒内引发多个事件,并且集合中可能有10000多个对象在这样做。我的想法是捕获最后一个事件,并在间隔1秒后丢弃任何挂起的事件 每当任何对象引发任何事件时,都会调用以下RefreshCollection()函数 SemaphoreSlim _semaphoreUpdatingList = new SemaphoreSlim(1); SemaphoreSlim _semaphoreRefreshingView = new Sema
RefreshCollection()
函数
SemaphoreSlim _semaphoreUpdatingList = new SemaphoreSlim(1);
SemaphoreSlim _semaphoreRefreshingView = new SemaphoreSlim(1);
CancellationTokenSource _ctsRefreshView = null;
internal void RefreshCollection()
{
// if we're in the process of changing the collection, return
if (_semaphoreUpdatingList.CurrentCount == 0)
{
return;
}
if (_ctsRefreshView != null)
{
_ctsRefreshView.Cancel();
}
Task.Run(async () =>
{
if (_ctsRefreshView == null)
{
_ctsRefreshView = new CancellationTokenSource();
}
var ct = _ctsRefreshView.Token;
try
{
await _semaphoreRefreshingView.WaitAsync(ct);
var stopWatch = new Stopwatch();
stopWatch.Start();
Application.Current?.Dispatcher?.Invoke(() =>
{
CollectionView.Refresh();
});
stopWatch.Stop();
// Only refresh every 1 sec
if (stopWatch.ElapsedMilliseconds < 1000)
{
await Task.Delay(1000 - (int)stopWatch.ElapsedMilliseconds);
}
_semaphoreRefreshingView.Release();
}
catch (OperationCanceledException)
{
return;
}
finally
{
_ctsRefreshView = null;
}
});
}
SemaphoreSlim\u semaphoreUpdateGlist=new SemaphoreSlim(1);
SemaphoreSlim_semaphoreRefreshingView=新的SemaphoreSlim(1);
CancellationTokenSource _ctsRefreshView=null;
内部无效刷新集合()
{
//如果我们正在更改集合,请返回
if(_semaphoreUpdateGlist.CurrentCount==0)
{
返回;
}
如果(_ctsRefreshView!=null)
{
_ctsRefreshView.Cancel();
}
Task.Run(异步()=>
{
如果(_ctsRefreshView==null)
{
_ctsRefreshView=新的CancellationTokenSource();
}
var ct=\u ctsRefreshView.Token;
尝试
{
wait_semaphoreRefreshingView.WaitAsync(ct);
var stopWatch=新秒表();
秒表。开始();
Application.Current?.Dispatcher?.Invoke(()=>
{
CollectionView.Refresh();
});
秒表;
//仅每1秒刷新一次
如果(秒表时间延迟毫秒<1000)
{
等待任务。延迟(1000-(整数)秒表。延迟毫秒);
}
_semaphoreRefreshingView.Release();
}
捕获(操作取消异常)
{
返回;
}
最后
{
_ctsRefreshView=null;
}
});
}
问题是,在调用此var ct=\u ctsRefreshView.Token时,任务内部很少出现\u ctsRefreshView为null
错误代码>。我在挠头,想知道为什么会发生这种事
非常感谢您的帮助。您应该使用Microsoft的反应式框架(又名Rx)-NuGetSystem.Reactive.Windows.Threading
(对于WPF位),并使用System.Reactive.Linq添加代码>
如果你有一个集合
这个类:
public class MyObject
{
public event EventHandler Ping;
}
然后你可以这样做:
IObservable<EventPattern<EventArgs>> query =
collection
.ToObservable()
.SelectMany(x =>
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => x.Ping += h,
h => x.Ping -= h))
.Sample(TimeSpan.FromSeconds(0.1))
.ObserveOnDispatcher();
IDisposable subscription = query.Subscribe(x => CollectionView.Refresh());
IObservable查询=
收集
.TooObservable()文件
.SelectMany(x=>
可观察
.FromEventPattern(
h=>x.Ping+=h,
h=>x.Ping-=h)
.样本(时间跨度从秒(0.1))
.ObserveOnDispatcher();
IDisposable subscription=query.subscripte(x=>CollectionView.Refresh());
这将使您最多每隔0.1
秒调用一次CollectionView.Refresh()
这比到处乱搞取消代币来源要容易得多
只需调用subscription.Dispose()
停止这一切。你应该使用微软的反应式框架(又名Rx)-NuGetSystem.Reactive.Windows.Threading
(对于WPF位)并使用System.Reactive.Linq添加代码>
如果你有一个集合
这个类:
public class MyObject
{
public event EventHandler Ping;
}
然后你可以这样做:
IObservable<EventPattern<EventArgs>> query =
collection
.ToObservable()
.SelectMany(x =>
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => x.Ping += h,
h => x.Ping -= h))
.Sample(TimeSpan.FromSeconds(0.1))
.ObserveOnDispatcher();
IDisposable subscription = query.Subscribe(x => CollectionView.Refresh());
IObservable查询=
收集
.TooObservable()文件
.SelectMany(x=>
可观察
.FromEventPattern(
h=>x.Ping+=h,
h=>x.Ping-=h)
.样本(时间跨度从秒(0.1))
.ObserveOnDispatcher();
IDisposable subscription=query.subscripte(x=>CollectionView.Refresh());
这将使您最多每隔0.1
秒调用一次CollectionView.Refresh()
这比到处乱搞取消代币来源要容易得多
只需调用subscription.Dispose()代码>来停止这一切。有一件事让我难以置信:为什么你不在这个范围之外实例化\u ctsRefreshView
令牌源代码?为什么要进行这种懒惰的实例化?而且,您没有等待任务。Run()
。如果您在一个任务尚未完成但另一个任务已启动的情况下背靠背运行此方法,则可能会导致一些奇怪的行为。这看起来是RX去抖动/节流的完美工作,可以在几行代码中完成。。。寻呼@Enigmativity@MichaelRandall-当然。这正是我所想的。@MichaelRandall-我已经提出了一个解决方案。这非常简单。有一件事让我难以置信:为什么你不在这个范围之外实例化\u ctsRefreshView
令牌源代码?为什么要进行这种懒惰的实例化?而且,您没有等待任务。Run()
。如果您在一个任务尚未完成但另一个任务已启动的情况下背靠背运行此方法,则可能会导致一些奇怪的行为。这看起来是RX去抖动/节流的完美工作,可以在几行代码中完成。。。寻呼@Enigmativity@MichaelRandall-当然。这正是我所想的。@MichaelRandall-我已经提出了一个解决方案。非常简单。