为什么不';t TaskScheduler.FromCurrentSynchronizationContext在M
本文关键字:FromCurrentSynchronizationContext TaskScheduler 为什么不 | 更新日期: 2023-09-27 18:28:20
我感兴趣的是,为什么我们需要调用InvokeOnMainThread,而这将是TaskScheduler.FromCurrentSynchronizationContext()的主要意图和职责?。
我正在使用Monotouch中的TPL作为iPhone应用程序来执行一些后台任务,并通过报告程序类更新UI。但似乎TaskScheduler.FromCurrentSynchronizationContext()并没有像您所期望的那样同步到UI线程。此时,我通过使用InvokeOnMainThread使其正常工作(但仍然感觉不对),正如Xamarin网站上的线程主题所描述的那样。
我还在BugZilla上发现了一个报告的(类似的)错误,似乎已经解决了。。以及关于在MonoTouch中使用背景线程的首选方式的另一个线程问题。
下面的代码片段说明了我的问题并展示了我的行为。
private CancellationTokenSource cancellationTokenSource;
private void StartBackgroundTask ()
{
this.cancellationTokenSource = new CancellationTokenSource ();
var cancellationToken = this.cancellationTokenSource.Token;
var progressReporter = new ProgressReporter ();
int n = 100;
var uiThreadId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine ("Start in thread " + uiThreadId);
var task = Task.Factory.StartNew (() =>
{
for (int i = 0; i != n; ++i) {
Console.WriteLine ("Work in thread " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep (30);
progressReporter.ReportProgress (() =>
{
Console.WriteLine ("Reporting in thread {0} (should be {1})",
Thread.CurrentThread.ManagedThreadId,
uiThreadId);
this.progressBar.Progress = (float)(i + 1) / n;
this.progressLabel.Text = this.progressBar.Progress.ToString();
});
}
return 42; // Just a mock result
}, cancellationToken);
progressReporter.RegisterContinuation (task, () =>
{
Console.WriteLine ("Result in thread {0} (should be {1})",
Thread.CurrentThread.ManagedThreadId,
uiThreadId);
this.progressBar.Progress = (float)1;
this.progressLabel.Text = string.Empty;
Util.DisplayMessage ("Result","Background task result: " + task.Result);
});
}
而报表类有这些方法
public void ReportProgress(Action action)
{
this.ReportProgressAsync(action).Wait();
}
public Task ReportProgressAsync(Action action)
{
return Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
public Task RegisterContinuation(Task task, Action action)
{
return task.ContinueWith(() => action(), CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
public Task RegisterContinuation<TResult>(Task<TResult> task, Action action)
{
return task.ContinueWith(() => action(), CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
应用程序输出窗口中的结果为:
Start in thread 1
Work in thread 6
Reporting in thread 6 (should be 1)
Work in thread 6
Reporting in thread 6 (should be 1)
...
Result in thread 1 (should be 1)
正如您所看到的,"在线程6中工作"很好。报告也在线程6上,这是错误的。有趣的是RegisterContinuation
在线程1中进行报告!!!
进展:我还没有弄清楚这个。。任何人
我认为问题在于您通过执行TaskScheduler.FromCurrentSynchronizationContext()
从ProgressReporter类中检索任务调度程序。
您应该将一个任务调度程序传递到ProgressReporter中,并使用该调度程序:
public class ProgressReporter
{
private readonly TaskScheduler taskScheduler;
public ProgressReporter(TaskScheduler taskScheduler)
{
this.taskScheduler = taskScheduler;
}
public Task RegisterContinuation(Task task, Action action)
{
return task.ContinueWith(n => action(), CancellationToken.None,
TaskContinuationOptions.None, taskScheduler);
}
// Remaining members...
}
通过将从UI线程获取的任务调度程序传递到进度报告程序,您可以确保任何报告都是在UI线程上完成的:
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
ProgressReporter progressReporter = new ProgressReporter(uiScheduler);
您使用的是什么版本的MonoTouch,以及的输出是什么:TaskScheduler.FromCurrentSynchronizationContext().GetType().ToString()。如果上下文已正确注册,则它应该是UIKitSynchronizationContext类型的类。如果这是一个正确类型的上下文,您可以通过直接调用上下文上的Post和Send方法来进行快速测试,看看它们最终是否在正确的线程上执行。您需要启动几个线程池线程来测试它是否正常工作,但它应该相当简单。