我对以下代码的行为有几件事情(但有一件主要事情)不了解。
有人可以帮忙解释一下吗?
它实际上是非常简单的代码-只是一个常规方法调用异步方法。在异步方法中,我使用using块尝试临时更改SynchronizationContext。
在代码的不同点,我探查了当前的SynchronizationContext。
这是我的问题:
当执行到达位置“ 2.1”时,上下文已更改为
上下文2。好的。然后,因为我们点击了“ await”,所以任务是
返回并执行跳回到位置“ 1.2”。为什么然后
位置1.2,上下文是否不“粘在”上下文2?
也许using语句和异步方法正在发生一些魔术?
在位置2.2,为什么上下文不是上下文#2?上下文是否不应该延续到“继续”(“ await”之后的语句)中?
码:
public class Test
{
public void StartHere()
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
this.logCurrentSyncContext("1.1"); // Context #1
Task t = f();
this.logCurrentSyncContext("1.2"); // Context #1, why not Context #2?
t.Wait();
this.logCurrentSyncContext("1.3"); // Context #1
}
private async Task f()
{
using (new ThreadPoolSynchronizationContextBlock())
{
this.logCurrentSyncContext("2.1"); // Context #2
await Task.Delay(7000);
this.logCurrentSyncContext("2.2"); // Context is NULL, why not Context #2?
}
this.logCurrentSyncContext("2.3"); // Context #1
}
// Just show the current Sync Context. Pass in some kind of marker so we know where, in the code, the logging is happening
private void logCurrentSyncContext(object marker)
{
var sc = System.Threading.SynchronizationContext.Current;
System.Diagnostics.Debug.WriteLine(marker + " Thread: " + Thread.CurrentThread.ManagedThreadId + " SyncContext: " + (sc == null? "null" : sc.GetHashCode().ToString()));
}
public class ThreadPoolSynchronizationContextBlock : IDisposable
{
private static readonly SynchronizationContext threadpoolSC = new SynchronizationContext();
private readonly SynchronizationContext original;
public ThreadPoolSynchronizationContextBlock()
{
this.original = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(threadpoolSC);
}
public void Dispose()
{
SynchronizationContext.SetSynchronizationContext(this.original);
}
}
}
结果:
1.1 Thread: 9 SyncContext: 37121646 // I call this "Context #1"
2.1 Thread: 9 SyncContext: 2637164 // I call this "Context #2"
1.2 Thread: 9 SyncContext: 37121646
2.2 Thread: 11 SyncContext: null
2.3 Thread: 11 SyncContext: 37121646
1.3 Thread: 9 SyncContext: 37121646
参考方案
2.2
解释很简单,1.2
不那么容易。
2.2
打印null
的原因是由于当您使用默认(await
)或new SynchronizationContext
SynchronizationContext null
时,将调用Post
方法传入延续委托,即is scheduled on the ThreadPool。当它们在ThreadPool(实际上是)上运行时,它们不依赖于当前SynchronizationContext
作为null
的这些继续,因此无需进行任何操作来还原当前实例。需要明确的是,由于您没有使用.ConfigureAwait(false)
,您的延续将被发布到捕获的上下文中,正如您所期望的那样,但是此实现中的Post
方法不会保留/流动相同的实例。
要解决此问题(即,使上下文变为“粘性”),您可以继承SynchronizationContext
,并重载Post
方法以使用已发布的委托调用SynchronizationContext.SetSynchronizationContext(this)
(使用Delegate.Combine(...)
)。此外,内部对象在大多数地方将SynchronizationContext
实例与null
相同,因此,如果您想玩这些东西,请务必创建一个继承实现。
对于1.2
,这实际上也让我感到惊讶,因为我的理解是这将调用底层状态机(以及AsyncMethodBuilder
中的所有内部信息),但是在保持其SynchronizationContext
的同时会被同步调用。
我认为我们在这里看到的内容已在in this post中进行了解释,这与在AsyncMethodBuilder
/异步状态机内部捕获并恢复ExecutionContext有关,这是在保护和保留调用ExecutionContext
并因此保留SynchronizationContext
。此can been seen here的代码(感谢@VMAtm)。
我正在使用Retrofit来获取JSON答复。这是我实施的一部分-@GET("/api/report/list") Observable<Bills> listBill(@Query("employee_id") String employeeID); 而条例草案类是-public static class…
改造正在返回一个空的响应主体 - java我正在尝试使用Retrofit和Gson解析一些JSON。但是,我得到的响应机构是空的。当我尝试从对象中打印信息时,出现NullPointerException。我确保URL正确,并且我也确保POJO也正确。我正在使用jsonschema2pojo来帮助创建POJO类。这是我要解析的JSON{ "?xml": { "@versi…
jQuery删除和$(this) - php我在remove()方法上遇到问题。我无法删除$(this)对象。我的代码是:$(".submit_add_type").live("click", function() { var parent = $(this).parent(); var type_value = parent.children('.t…
如何正确增加喜欢和不喜欢的人 - javascript我在每个帖子上都有一个按钮1)点赞按钮如果用户已经喜欢了该帖子,则显示“与众不同”按钮如果用户不喜欢该帖子,则为HTML部分<a href="javascript:void(0);" id="liker" data-count="0" data-fpc="481" data…
每个文件合并后添加换行 - python我有很多类似以下内容的JSON文件:例如。1.json{"name": "one", "description": "testDescription...", "comment": ""} test.json{"name"…