C# .net core AsyncLocal与System.Responsive失去上下文

C# .net core AsyncLocal与System.Responsive失去上下文,c#,.net,system.reactive,executioncontext,callcontext,C#,.net,System.reactive,Executioncontext,Callcontext,我想使用AsyncLocal通过异步工作流传递信息以进行跟踪。现在我遇到了RX的问题。 Thios是我的测试代码: using System; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Threading; using System.Threading.Tasks; public class RxTest { private readonly Subject<int> t

我想使用AsyncLocal通过异步工作流传递信息以进行跟踪。现在我遇到了RX的问题。
Thios是我的测试代码:

using System;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading;
using System.Threading.Tasks;

public class RxTest
{
    private readonly Subject<int> test = new Subject<int>();

    private readonly AsyncLocal<int> asyncContext = new AsyncLocal<int>();

    public void Test()
    {
        this.test
             // .ObserveOn(Scheduler.Default)
            .Subscribe(this.OnNextNormal);
        this.test
             // .ObserveOn(Scheduler.Default)
            .Delay(TimeSpan.FromMilliseconds(1))
            .Subscribe(this.OnNextDelayed);

        for (var i = 0; i < 2; i++)
        {
            var index = i;
            Task.Run(() =>
            {
                this.asyncContext.Value = index;
                Console.WriteLine(
                    $"Main\t\t{index} (Thread: {Thread.CurrentThread.ManagedThreadId}): AsyncLocal.Value => {this.asyncContext.Value}");
                this.test.OnNext(index);
            });
        }

        Console.ReadKey();
    }

    private void OnNextNormal(int obj)
    {
        Console.WriteLine(
            $"OnNextNormal\t{obj} (Thread: {Thread.CurrentThread.ManagedThreadId}): AsyncLocal.Value => {this.asyncContext.Value}");
    }

    private void OnNextDelayed(int obj)
    {
        Console.WriteLine(
            $"OnNextDelayed\t{obj} (Thread: {Thread.CurrentThread.ManagedThreadId}): AsyncLocal.Value => {this.asyncContext.Value}");
    }
}
使用系统;
使用System.Reactive.Linq;
使用系统、反应、主题;
使用系统线程;
使用System.Threading.Tasks;
公共类RxTest
{
私有只读主题测试=新主题();
私有只读AsyncLocal asyncContext=new AsyncLocal();
公开无效测试()
{
这个测试
//.ObserveOn(Scheduler.Default)
.订阅(此.OnNextNormal);
这个测试
//.ObserveOn(Scheduler.Default)
.延迟(时间跨度从毫秒(1))
.订阅(此.OnNextDelayed);
对于(变量i=0;i<2;i++)
{
var指数=i;
Task.Run(()=>
{
this.asyncContext.Value=索引;
控制台写入线(
$“Main\t\t{index}(线程:{Thread.CurrentThread.ManagedThreadId}):AsyncLocal.Value=>{this.asyncContext.Value}”);
this.test.OnNext(索引);
});
}
Console.ReadKey();
}
专用void OnNextNormal(内部对象)
{
控制台写入线(
$“OnNextNormal\t{obj}(线程:{Thread.CurrentThread.ManagedThreadId}):AsyncLocal.Value=>{this.asyncContext.Value}”);
}
私有无效ONNEXTDELATED(内部obj)
{
控制台写入线(
$“OnNextDelayed\t{obj}(线程:{Thread.CurrentThread.ManagedThreadId}):AsyncLocal.Value=>{this.asyncContext.Value}”);
}
}
输出为:

主0(线程:5):AsyncLocal.Value=>0
Main 1(线程:6):AsyncLocal.Value=>1
OnNextNormal 0(线程:5):AsyncLocal.Value=>0
OnNextNormal 1(线程:6):AsyncLocal.Value=>1
OnNextDelayed 0(线程:4):AsyncLocal.Value=>0
OnNextDelayed 1(线程:4):AsyncLocal.Value=>0

如您所见,AsyncLocal.Value不会流向延迟订阅的方法。
=>异步值在延迟轨道上丢失

据我所知,普通Subscribe()不使用调度程序,Delay()使用调度程序。
当我对两个调用都使用ObserveOn()时,两个调用的输出如下

主0(线程:5):AsyncLocal.Value=>0
Main 1(线程:7):AsyncLocal.Value=>1
OnNextNormal 0(线程:9):AsyncLocal.Value=>0
OnNextNormal 1(线程:9):AsyncLocal.Value=>0
OnNextDelayed 0(线程:4):AsyncLocal.Value=>0
OnNextDelayed 1(线程:4):AsyncLocal.Value=>0

=>AsyncValue在每首曲目上都会丢失

有没有办法让ExecutionContext与RX一起流动?
我只发现了问题的另一面。他们解决了观察者上下文如何流动的问题。我想在发布者的上下文中流动

我想要实现的是:

  • 来自“外部”的信息为我服务
  • 在服务内分发消息(RX)
  • 记录消息时,请使用MessageId格式化日志消息
  • 我不想把这个消息到处传

  • 提前感谢您的回答。

    Rx中的自由流动执行上下文使其适用于大多数多线程场景。您可以通过绕过计划的方法来强制线程上下文,如下所示:

    public static class Extensions
    {
        public static IObservable<T> TaskPoolDelay<T>(this IObservable<T> observable, TimeSpan delay)
        {
            return Observable.Create<T>(
                observer => observable.Subscribe(
                    onNext: value => Task.Delay(delay).ContinueWith(_ => observer.OnNext(value)),
                    onError: observer.OnError,
                    onCompleted: observer.OnCompleted
                )
            );
        }
    }
    

    这确实会推进上下文,但对于较大的查询,它很快就会变得复杂。我不确定实现一个
    IScheduler
    ,它在通知时保留上下文是否能很好地工作。如果消息复制没有太多开销,那么这可能最适合Rx

    用于
    this.test.Subscribe(this.OnNextNormal)
    的默认计划程序是
    scheduler.Immediate
    。一旦引入任何形式的并发,您就可以立即更改调度程序。我怀疑这就是导致
    AsyncLocal
    无法工作的原因。您需要找到另一个解决方案,除非您只处理最简单的Rx查询。
    OnNextDelayed   2 (Thread: 6): AsyncLocal.Value => 2
    OnNextDelayed   3 (Thread: 10): AsyncLocal.Value => 3
    OnNextDelayed   1 (Thread: 7): AsyncLocal.Value => 1
    OnNextDelayed   0 (Thread: 5): AsyncLocal.Value => 0