注意:这是在Unity3D下
我需要使用自定义同步上下文运行内部任务,如下所示:
- 外部任务 -> 在默认调度程序上
- 内部任务 -> 具有自定义同步上下文的自定义调度程序
这是因为内部任务中的某些对象只能在自定义同步上下文将发布到的特定线程上创建,另一方面,我希望外部任务正常运行,即ThreadPool
或任何 Task.Run
使用的内容。
以下问题的建议:
How to run a Task on a custom TaskScheduler using await?
不幸的是,这不起作用,同步上下文仍然是默认的:
OUTER JOB: 'null'
0
INNER JOB: 'null' <------------- this context should not be the default one
0, 1, 2, 3, 4,
1
INNER JOB: 'null'
0, 1, 2, 3, 4,
2
INNER JOB: 'null'
0, 1, 2, 3, 4,
Done, press any key to exit
代码:
using System;
using System.Threading;
using System.Threading.Tasks;
internal static class Program
{
private static TaskFactory CustomFactory { get; set; }
private static async Task Main(string[] args)
{
// create a custom task factory with a custom synchronization context,
// and make sure to restore initial context after it's been created
var initial = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(new CustomSynchronizationContext());
CustomFactory = new TaskFactory(
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext()
);
SynchronizationContext.SetSynchronizationContext(initial);
// now do some work on initial context
await Task.Run(async () =>
{
Console.WriteLine($"OUTER JOB: {GetCurrentContext()}");
for (var i = 0; i < 3; i++)
{
Console.WriteLine(i);
await Task.Delay(100);
// now do some work on custom context
await RunOnCustomScheduler(DoSpecialWork);
}
});
Console.WriteLine("Done, press any key to exit");
Console.ReadKey();
}
private static void DoSpecialWork()
{
Console.WriteLine($"\tINNER JOB: {GetCurrentContext()}"); // TODO wrong context
for (var i = 0; i < 5; i++)
{
Thread.Sleep(250);
Console.Write($"\t{i}, ");
}
Console.WriteLine();
}
private static Task RunOnCustomScheduler(Action action)
{
return CustomFactory.StartNew(action);
}
private static string GetCurrentContext()
{
return SynchronizationContext.Current?.ToString() ?? "'null'";
}
}
public class CustomSynchronizationContext : SynchronizationContext
{
}
当我调试时,我可以看到自定义工厂确实具有带有自定义上下文的自定义调度程序,但实际上它不起作用。
问题:
如何让我的自定义调度程序使用从中创建的自定义上下文?
最终答案:
我正在等待线程内的一个方法,我想避免在其中工作,将这些内容包装在 await Task.Run
中(加上一些为了简洁而省略的细节)修复了它。现在,我可以在 Unity 线程之外运行长任务,但仍然使用上面提到的自定义工厂方法在其中进行 Unity 调用。
即它已经可以工作了,但我没有正确使用它
最佳答案
您的CustomSynchronizationContext
确实用于运行 DoSpecialWork
方法。您可以通过覆盖 Post
来确认这一点基类的方法:
public class CustomSynchronizationContext : SynchronizationContext
{
public override void Post(SendOrPostCallback d, object state)
{
Console.WriteLine($"@Post");
base.Post(d, state);
}
}
然后再次运行您的程序,您将看到控制台中出现三个@Post。
原因是 SynchronizationContext.Current
是 null
是因为这个属性是与当前线程关联的。这是source code :
// Get the current SynchronizationContext on the current thread
public static SynchronizationContext Current
{
get
{
return Thread.CurrentThread.GetExecutionContextReader().SynchronizationContext ?? GetThreadLocalContext();
}
}
您可以安装 CustomSynchronizationContext
在 ThreadPool
的每个线程中,但不建议这样做。这也不是必需的。在没有SynchronizationContext.Current
的情况下异步延续正在当前 TaskScheduler
中运行,以及当前的TaskScheduler
里面DoSpecialWorkAsync
方法是您使用 TaskScheduler.FromCurrentSynchronizationContext()
自己创建的方法方法,与您的 CustomSynchronizationContext
相关联实例。
关于c# - 自定义TaskFactory不使用自定义SynchronizationContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61235015/