正在伺机应该恢复Thread.CurrentContext? [英] Is await supposed to restore Thread.CurrentContext?

查看:280
本文介绍了正在伺机应该恢复Thread.CurrentContext?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

相关这个问题

等待应该恢复环境(特别是再由 Thread.CurrentContext psented $ P $的上下文中)为 ContextBoundObject ?考虑下面的:

Is await supposed to restore the context (specifically the context represented by Thread.CurrentContext) for a ContextBoundObject? Consider the below:

class Program
{
    static void Main(string[] args)
    {
        var c1 = new Class1();
        Console.WriteLine("Method1");
        var t = c1.Method1();
        t.Wait();

        Console.WriteLine("Method2");
        var t2 = c1.Method2();
        t2.Wait();
        Console.ReadKey();
    }
}

public class MyAttribute : ContextAttribute
{
    public MyAttribute() : base("My") { }
}

[My]
public class Class1 : ContextBoundObject
{
    private string s { get { return "Context: {0}"; } } // using a property here, since using a field causes things to blow-up.

    public async Task Method1()
    {
        Console.WriteLine(s, Thread.CurrentContext.ContextID); // Context1
        await Task.Delay(50);
        Console.WriteLine(s, Thread.CurrentContext.ContextID); // Context0
    }

    public Task Method2()
    {
        Console.WriteLine(s, Thread.CurrentContext.ContextID); // Context1
        return Task.Delay(50).ContinueWith(t => Console.WriteLine(s, Thread.CurrentContext.ContextID)); // Context1
    }
}


  • 异步 / 等待情况下,上下文是不可恢复的,因此剩下的$ C $ç后的await结束了在不同的上下文中执行。

    • In the async/await case, the context isn't restored, so the remaining code after the await ends up executing in a different context.

      .ContinueWith 的情况下,上下文不被TPL恢复,而是上下文最终得到恢复,由于这样的事实:在lambda最终被翻在地类的成员方法。有拉姆达没有使用一个成员变量,上下文不会在这种情况下,无论是恢复

      In the .ContinueWith case, the context isn't restored by the tpl, but instead the context ends up getting restored due to the fact that the lambda ends up being turned in to class member method. Had the lambda not used a member variable, the context wouldn't be restored in that case either.

      看来,缘于此,使用异步 / 等待 ContextBoundObject延续旨意导致意外beahvior。例如,考虑一下,如果我们使用了 [同步] 属性(<一个href=\"http://msdn.microsoft.com/en-us/library/system.runtime.remoting.contexts.synchronizationattribute%28v=vs.110%29.aspx\"相对=关于使用异步 / nofollow的> MSDN文档)等待。后同步担保并不适用于code中的第一个等待

      It seems that due to this, using async/await or continuations with ContextBoundObjects will result in unexpected beahvior. For example, consider if we had used the [Synchronization] attribute (MSDN doc) on a class that uses async/await. The Synchronization guarantees would not apply to code after the first await.

      在应对@Noseratio

      ContextBoundObjects 不(一定或默认)要求线程关联。在这个例子中,我给那里的情况下最终被同样的,你最终不会是相同的线程上(除非你是幸运的)。您可以使用 Context.DoCallBack(...)来在上下文中找到工作。这会不会让你到原来的线程(除非上下文会替你)。下面是修改 1级这表明:

      ContextBoundObjects don't (necessarily or by default) require thread affinity. In the example, I gave where the context ends up being the same, you don't end up being on the same thread (unless you are lucky). You can use Context.DoCallBack(...) to get work within a context. This won't get you on to the original thread (unless the Context does that for you). Here's a modification of Class1 demonstrating that:

          public async Task Method1()
          {
              var currCtx = Thread.CurrentContext;
              Console.WriteLine(s, currCtx.ContextID); // Context1
              Console.WriteLine("Thread Id: {0}", Thread.CurrentThread.ManagedThreadId);
              await Task.Delay(50);
              currCtx.DoCallBack(Callback);
          }
      
          static void Callback()
          {
              Console.WriteLine("Context: {0}", Thread.CurrentContext.ContextID); // Context1
              Console.WriteLine("Thread Id: {0}", Thread.CurrentThread.ManagedThreadId);
          }
      

      如果等待被恢复的背景下,我的期望不会是上下文会被复制到新的线程,而是将类似于如何在的SynchronizationContext 恢复。基本上,你会希望将当前上下文的等待,然后你想要的部分后,通过调用执行的await capturedContext被捕获.DoCallback(afterAwaitWork)

      If await were to restore the Context, my expectation would not be that the Context would be "copied" to the new thread, but rather it would be similar to how the SynchronizationContext is restored. Basically, you would want the current Context to be captured at the await, and then you would want the part after the await to be executed by calling capturedContext.DoCallback(afterAwaitWork).

      DoCallback 确实恢复环境的工作。正是恢复上下文的工作是依赖于特定上下文

      The DoCallback does the work of restoring the context. Exactly what the work of the restoring the context is is dependent on the specific context.

      ,似乎它的可能的可以通过创建一个自定义来获得这一行为的SynchronizationContext 它包装张贴到任何工作在调用 DoCallback

      Based on that, it seems that it might be possible to get this behavior by creating a custom SynchronizationContext which wraps any work posted to it in a call to DoCallback.

      推荐答案

      显然, Thread.CurrentContext 没有得到流动。这是有趣的,看看什么是真正得到流动为的ExecutionContext 的一部分,<一个href=\"http://referencesource.microsoft.com/#mscorlib/system/threading/executioncontext.cs#3a777fa35a9f1ffe\"相对=在.NET参考来源nofollow的>在这里。特别有趣的是如何同步的情况下被通过 ExecutionContext.Run 明确流向,而不是隐式 Task.Run

      Apparently, Thread.CurrentContext doesn't get flowed. It's interesting to see what actually gets flowed as part of ExecutionContext, here in .NET reference sources. Especially interesting how synchronization context gets flowed explicitly via ExecutionContext.Run, but not implicitly with Task.Run.

      我不知道定制同步上下文(如 AspNetSynchronizationContext ),这可能比的ExecutionContext (默认设置)。

      I'm not sure about customized synchronization contexts (e.g. AspNetSynchronizationContext), which may flow more thread's properties than ExecutionContext does by default.

      下面是一个伟大的阅读,相关的:<一href=\"http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx\"相对=nofollow>的ExecutionContext VS的SynchronizationContext。

      Here is a great read, related: "ExecutionContext vs SynchronizationContext".

      更新后,它不会出现 Thread.CurrentContext 可在所有流过,的甚至如果你想手工做(的东西,如斯蒂芬Toub的的 WithCurrentCulture )。检查实施<一个href=\"http://referencesource.microsoft.com/#mscorlib/system/runtime/remoting/context.cs#28f5043928414712\"相对=nofollow> System.Runtime.Remoting.Contexts.Context ,显然它不是设计被复制到另一个线程(不像的SynchronizationContext 的ExecutionContext )。

      Updated, it doesn't appear that Thread.CurrentContext could be flowed at all, even if you wanted to do it manually (with something like Stephen Toub's WithCurrentCulture). Check the implementation of System.Runtime.Remoting.Contexts.Context, apparently it is not designed to be copied to another thread (unlike SynchronizationContext or ExecutionContext).

      我不是.NET远程的专家,但我觉得 ContextBoundObject 派生的对象都需要线程关联。即,他们得到创建,访问并摧毁了同一个线程,对他们的一生。我相信这是 ContextBoundObject 设计要求的一部分。

      I'm not an expert in .NET remoting, but I think ContextBoundObject-derived objects require thread affinity. I.e., they get created, accessed and destroyed on the same thread, for their lifetime. I believe this is a part of ContextBoundObject design requirements.

      更新的基础上,@马特·史密斯的更新

      马特,your're绝对正确的,有一个 ContextBoundObject 没有线程亲和力为基础的对象时,它是从不同的域调用。在不同的线程或上下文整个对象的访问被序列化,如果 [同步] 的类指定的。

      Matt, your're absolutely right, there is no thread affinity for ContextBoundObject-based objects when it's called from a different domain. The access to the whole object across different threads or contexts gets serialized if [Synchronization] is specified on the class.

      还有线程和上下文之间没有逻辑联系,据我可以告诉。上下文是与对象关联的东西。可以有相同的线程上运行多个上下文(不像COM公寓)和多线程共享相同的上下文(类似于COM公寓)。

      There is also no logical connection between threads and contexts, as far as I can tell. A context is something associated with an object. There can be multiple contexts running on the same thread (unlike with COM apartments), and multiple threads sharing the same context (similar to COM apartments).

      使用 Context.DoCallback ,确实有可能继续在同一环境后等待,无论是与一个定制awaiter(如下code完成),或用自定义同步情况下,你在你的问题中提到。

      Using Context.DoCallback, it is indeed possible to continue on the same context after await, either with a custom awaiter (as done in the code below), or with a custom synchronization context, as you mentioned in your question.

      在code我打了:

      using System;
      using System.Runtime.Remoting.Contexts;
      using System.Threading;
      using System.Threading.Tasks;
      
      namespace ConsoleApplication
      {
          public class Program
          {
              [Synchronization]
              public class MyController: ContextBoundObject
              {
                  /// All access to objects of this type will be intercepted
                  /// and a check will be performed that no other threads
                  /// are currently in this object's synchronization domain.
      
                  int i = 0;
      
                  public void Test()
                  {
                      Console.WriteLine(String.Format("\nenter Test, i: {0}, context: {1}, thread: {2}, domain: {3}", 
                          this.i, 
                          Thread.CurrentContext.ContextID, 
                          Thread.CurrentThread.ManagedThreadId, 
                          System.AppDomain.CurrentDomain.FriendlyName));
      
                      Console.WriteLine("Testing context...");
                      Program.TestContext();
      
                      Thread.Sleep(1000);
                      Console.WriteLine("exit Test");
                      this.i++;
                  }
      
                  public async Task TaskAsync()
                  {
                      var context = Thread.CurrentContext;
                      var contextAwaiter = new ContextAwaiter();
      
                      Console.WriteLine(String.Format("TaskAsync, context: {0}, same context: {1}, thread: {2}",
                          Thread.CurrentContext.ContextID,
                          Thread.CurrentContext == context,
                          Thread.CurrentThread.ManagedThreadId));
      
                      await Task.Delay(1000);
                      Console.WriteLine(String.Format("after Task.Delay, context: {0}, same context: {1}, thread: {2}",
                          Thread.CurrentContext.ContextID,
                          Thread.CurrentContext == context,
                          Thread.CurrentThread.ManagedThreadId));
      
                      await contextAwaiter;
                      Console.WriteLine(String.Format("after await contextAwaiter, context: {0}, same context: {1}, thread: {2}",
                          Thread.CurrentContext.ContextID,
                          Thread.CurrentContext == context,
                          Thread.CurrentThread.ManagedThreadId));
                  }
              }
      
              // ContextAwaiter
              public class ContextAwaiter :
                  System.Runtime.CompilerServices.INotifyCompletion
              {
                  Context _context;
      
                  public ContextAwaiter()
                  {
                      _context = Thread.CurrentContext;
                  }
      
                  public ContextAwaiter GetAwaiter()
                  {
                      return this;
                  }
      
                  public bool IsCompleted
                  {
                      get { return false; }
                  }
      
                  public void GetResult()
                  {
                  }
      
                  // INotifyCompletion
                  public void OnCompleted(Action continuation)
                  {
                      _context.DoCallBack(() => continuation());
                  }
              }
      
              // Main
              public static void Main(string[] args)
              {
                  var ob = new MyController();
      
                  Action<string> newDomainAction = (name) =>
                  {
                      System.AppDomain domain = System.AppDomain.CreateDomain(name);
                      domain.SetData("ob", ob);
                      domain.DoCallBack(DomainCallback);
                  };
      
                  Console.WriteLine("\nPress Enter to test domains...");
                  Console.ReadLine();
      
                  var task1 = Task.Run(() => newDomainAction("domain1"));
                  var task2 = Task.Run(() => newDomainAction("domain2"));
                  Task.WaitAll(task1, task2);
      
                  Console.WriteLine("\nPress Enter to test ob.Test...");
                  Console.ReadLine();
                  ob.Test();
      
                  Console.WriteLine("\nPress Enter to test ob2.TestAsync...");
                  Console.ReadLine();
                  var ob2 = new MyController();
                  ob2.TaskAsync().Wait();
      
                  Console.WriteLine("\nPress Enter to test TestContext...");
                  Console.ReadLine();
                  TestContext();
      
                  Console.WriteLine("\nPress Enter to exit...");
                  Console.ReadLine();
              }
      
              static void DomainCallback()
              {
                  Console.WriteLine(String.Format("\nDomainCallback, context: {0}, thread: {1}, domain: {2}",
                      Thread.CurrentContext.ContextID,
                      Thread.CurrentThread.ManagedThreadId,
                      System.AppDomain.CurrentDomain.FriendlyName));
      
                  var ob = (MyController)System.AppDomain.CurrentDomain.GetData("ob");
                  ob.Test();
                  Thread.Sleep(1000);
              }
      
              public static void TestContext()
              {
                  var context = Thread.CurrentContext;
                  ThreadPool.QueueUserWorkItem(_ =>
                  {
                      Console.WriteLine(String.Format("QueueUserWorkItem, context: {0}, same context: {1}, thread: {2}",
                          Thread.CurrentContext.ContextID,
                          Thread.CurrentContext == context,
                          Thread.CurrentThread.ManagedThreadId));
                  }, null);
      
                  ThreadPool.UnsafeQueueUserWorkItem(_ =>
                  {
                      Console.WriteLine(String.Format("UnsafeQueueUserWorkItem, context: {0}, same context: {1}, thread: {2}",
                          Thread.CurrentContext.ContextID,
                          Thread.CurrentContext == context,
                          Thread.CurrentThread.ManagedThreadId));
                  }, null);
              }
          }
      }
      

      这篇关于正在伺机应该恢复Thread.CurrentContext?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆