我们有一个用WPF编写的带滚动条的视频播放器。当左右拖动滚动条时,CurrentFrameTime
会更新并触发UpdateFrames
,然后抓取框架并显示它。这很好。
但是,有时抓取框架可能需要一些时间(例如因为磁盘),虽然CurrentFrameTime
值已经改变,但UpdateFrames
可能会被卡住"并且仍在等待...GetAsync().Result
中的上一帧。
决定将Dispatcher.BeginInvoke
移至ContinueWith
区块。现在,每次更改CurrentFrameTime
时,前一个操作都将被取消(如果帧时间已经更改,我们不需要显示帧),并且应显示最新的帧。但是,由于某种原因,由于这种变化,应用程序变得更慢。当我拖动滚动时,可能需要几秒钟才能更新图像。
将代码移入ContinueWith
会导致视频播放器速度变慢的情况会怎样?
没有ContinueWith的MainApplication
_threadUpdateUI = new Thread(new ThreadStart(UpdateFrames));
public long CurrentFrameTime
{
get{...}
set
{
...
_fetchFrame.Set();
}
}
void UpdateFrames()
{
while(run)
{
_fetchFrame.WaitOne();
var frame = Cache.Default.GetAsync(CurrentFrameTime)
.Result;
Dispatcher.BeginInvoke(new Action(() => ShowFrame(frame.Time, frame.Image)));
}
}
缓存
public Task<VideoFrame> GetAsync(long frameTime)
{
//this i used when cache is disabled
if (GrabSynchronously)
{
var tcs = new TaskCompletionSource<VideoFrame>();
//reading from file
var frame2 = FrameProvider.Instance.GetFrame(frameTime);
tcs.SetResult(frame2);
return tcs.Task;
}
...
}
MainApplication WITH ContinueWith
void ShowFrames()
{
while(run)
{
_fetchFrame.WaitOne();
_previousFrameCancellationToken.Cancel();
_previousFrameCancellationToken = new CancellationTokenSource();
Cache.Default.GetAsync(CurrentFrameTime).ContinueWith((task) =>
{
var frameTime = task.Result.Time;
var frameImage = task.Result.Image
Dispatcher.BeginInvoke(new Action(() => ShowFrame(frameTime, frameImage)));
}, _previousFrameCancellationToken.Token);
}
}
DF
答案 0 :(得分:3)
以旧的方式,UpdateFrames
循环会阻止每次.Result
来电。这使你的循环自我测量,只允许一个请求&#34;在飞行中&#34;即使_fetchFrame
在.Set()
等待.Result
完成时多次调用_fetchFrame.Set()
,也会一次。
以新的方式,每次拨打GrabSynchronously
都会触发另一项启动任务,并在飞行中#34; (假设Semaphore _frameIsProcessing = new Semaphore(5, 5); //Allows for up to 5 frames to be requested at once before it starts blocking requests.
private void ShowFrames()
{
while (run)
{
_fetchFrame.WaitOne();
_previousFrameCancellationToken.Cancel();
_previousFrameCancellationToken = new CancellationTokenSource();
_frameIsProcessing.WaitOne();
Cache.Default.GetAsync(CurrentFrameTime).ContinueWith((task) =>
{
_frameIsProcessing.Release();
if(_previousFrameCancellationToken.IsCancellationRequested)
return;
var frameTime = task.Result.Time;
var frameImage = task.Result.Image;
Dispatcher.BeginInvoke(new Action(() => ShowFrame(frameTime, frameImage)));
});
}
}
为假)即使它永远不会被使用。这会使您的系统充满请求并导致您的速度减慢。
一种可能的解决方案是放置某种类型的另一个信号量来限制可以处理的帧的并发请求数。
{{1}}