我有接收文件列表的功能,并且可以在单个/单独的线程中工作:
public void DoWork(params...)
{
_filesInProcess = 0;
_filesFinished = 0;
_tokenSource = new CancellationTokenSource();
var token = _tokenSource.Token;
Task.Factory.StartNew(() =>
{
try
{
Parallel.ForEach(_indexedSource,
new ParallelOptions
{
MaxDegreeOfParallelism = parallelThreads //limit number of parallel threads
},
file =>
{
if (token.IsCancellationRequested)
return;
//do work...
});
}
catch (Exception)
{ }
}, _tokenSource.Token).ContinueWith(
t =>
{
//finish...
if (OnFinishWorkEventHandler != null)
OnFinishWorkEventHandler(this, EventArgs.Empty);
}
, TaskScheduler.FromCurrentSynchronizationContext() //to ContinueWith (update UI) from UI thread
);
}
我添加了在循环中运行此命令的选项,在此函数完成后从主窗体运行,如果我需要另一个循环,我再次调用此函数,但在第二次冻结我的所有UI,我无法找到为什么
这就是我打电话给我的功能:
private void StartJob()
{
string[] files = GetlFiles().ToArray();
Job job = new Job(files);
timerStatus.Enabled = true;
job.OnStartPlayEventHandler += job_OnStartPlayEventHandler;
job.OnFinishPlayEventHandler += job_OnFinishPlayEventHandler;
job.OnFinishWorkEventHandler += job_OnFinishWorkEventHandler;
job.StartTimerEventHandler += job_StartTimerEventHandler;
job.StopTimerEventHandler += job_StopTimerEventHandler;
job.DoWork(
NetworkAdapter.SelectedAdapter.PacketDevice,
ReadSpeed(),
PlayOption.Regular,
ChecksumFixer.FixBadChecksum,
ReadParallelThreads(PlayState.Single),
Iteration.Loops
);
}
此操作完成后:
private void job_OnFinishWorkEventHandler(object sender, EventArgs e)
{
Job job = sender as Job;
job.OnStartPlayEventHandler -= job_OnStartPlayEventHandler;
job.OnFinishPlayEventHandler -= job_OnFinishPlayEventHandler;
job.OnFinishWorkEventHandler -= job_OnFinishWorkEventHandler;
job.StartTimerEventHandler -= job_StartTimerEventHandler;
job.StopTimerEventHandler -= job_StopTimerEventHandler;
Iteration.LoopFinished++;
if (Iteration.LoopFinished < Iteration.Loops) // In case i want another loop
StartJob();
else
{
UnlockButtonsAfterPlay();
UnlockContextMenuAfterPlay();
}
}
正如我提到的所有陷入第二次迭代
答案 0 :(得分:5)
Task.Factory.StartNew is dangerous。它使用TaskScheduler.Current
而不是TaskScheduler.Default
。
在您的情况下OnFinishWorkEventHandler
从ContinueWith
触发,该TaskScheduler.FromCurrentSynchronizationContext()
在TaskScheduler.Current
的UI线程中运行。所以此时TaskScheduler.Default
是UIScheduler而不是Task.Run
(线程池调度程序)。
这就是为什么你的第二个任务在UI线程中安排,导致UI冻结。
要解决此问题,请使用始终指向TaskScheduler.Default
的{{1}}。 Task.Run
是.net 4.5中的新功能,如果您使用.net 4.0,则可以使用默认参数创建TaskFactory
,然后就可以使用它。
private static readonly TaskFactory factory = new TaskFactory(CancellationToken.None,
TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
然后使用
factory.StartNew(...);
如果您只是在自己不需要创建工厂实例时才使用它,只需明确指定TaskScheduler
即可。
Task.Factory.StartNew(() =>{
//Your code here
}, _tokenSource.Token,
TaskCreationOptions.None,
TaskScheduler.Default)//Note TaskScheduler.Default here
.ContinueWith(
t =>
{
//finish...
if (OnFinishWorkEventHandler != null)
OnFinishWorkEventHandler(this, EventArgs.Empty);
}
, TaskScheduler.FromCurrentSynchronizationContext());