c# - 自定义TaskFactory不使用自定义SynchronizationContext

标签 c# unity-game-engine asynchronous task-parallel-library synchronizationcontext

注意:这是在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/

相关文章:

asynchronous - 从 OCaml/Async 中的延迟列表中获取任何非错误元素

c# - 在 C# 中生成整数集

c# - 如何在 C# 中按国家/地区获取语言

c# - Unity3D 声明变量或使用 GetComponent

asynchronous - 使用 OCaml Async 并发写入

javascript - $q.All 在级联时没有等待所有 promise 完成

c# - Entity Framework 4.1 修改对象和子集合

c# - Kim 和 Pearl 的贝叶斯网络中的消息传递算法

ios - Unity-Xcode错误: linker command failed with exit code 1 (use -v to see invocation)

android - 三星 Galaxy S8 上的 Unity/ARCore - 相机图像被拉伸(stretch)