- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在执行以下代码片段来测试我如何更改线程,在 await
之后我的代码在该线程上ing 将被调用。根据 @Stephen Cleary in this answer , 以便能够在 await
之后继续执行异步代码在同一个线程(上下文)上,我需要设置 SynchronizationContext
,我这样做了,但是,我的代码在不同的线程中继续运行。
static void Main(string[] args)
{
var mainSyncContex = new SynchronizationContext();
SynchronizationContext.SetSynchronizationContext(mainSyncContex);
Console.WriteLine($"Hello World! ThreadId: {Thread.CurrentThread.ManagedThreadId}"); // <-- In thread 1
try
{
Task.Run(async () =>
{
SynchronizationContext.SetSynchronizationContext(mainSyncContex);
Console.WriteLine($"Is there Sync Contex?: {SynchronizationContext.Current != null}");
Console.WriteLine($"Before delay. ThreadId: {Thread.CurrentThread.ManagedThreadId}"); // <-- In thread 3
await Task.Delay(1000).ConfigureAwait(true);
Console.WriteLine($"After delay. ThreadId: {Thread.CurrentThread.ManagedThreadId}"); // <-- In thread 4
throw new Exception();
});
}
catch (Exception e)
{
Console.WriteLine($"Exception: {e.Message} Catch. ThreadId: {Thread.CurrentThread.ManagedThreadId}");
}
Console.WriteLine($"Ending ThreadId: {Thread.CurrentThread.ManagedThreadId}"); // <-- In thread 1
Console.ReadKey();
}
输出:
Hello World! ThreadId: 1
Ending ThreadId: 1
Is there Sync Contex?: True
Before delay. ThreadId: 3
After delay. ThreadId: 4
最佳答案
我想根据我的理解展示一些代码,希望它可以帮助某人。
正如 aepot、dymanoid 和 Hans Passant(感谢他们)所说,使用默认 SynchronizationContext
只会做 Post
输入 await
之后的其余代码转至 SynchronizationContext
.
我创建了一个非常基本但不是最佳的 SynchronizationContext
演示基本实现的样子。我的实现将创建一个新的 Thread
并运行一些 Task
s 在同一个新创建的特定上下文中 Thread
.
可以找到更好的实现(但非常复杂)here在 Stephen Cleary 的 GitHub 存储库中。
我的实现基本上如下所示(来自 my GitHub repository ,存储库中的代码将来可能会有所不同):
/// <summary>
/// This <see cref="SynchronizationContext"/> will call all posted callbacks in a single new thread.
/// </summary>
public class SingleNewThreadSynchronizationContext : SynchronizationContext
{
readonly Thread _workerThread;
readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _actionStatePairs = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
/// <summary>
/// Returns the Id of the worker <see cref="Thread"/> created by this <see cref="SynchronizationContext"/>.
/// </summary>
public int ManagedThreadId => _workerThread.ManagedThreadId;
public SingleNewThreadSynchronizationContext()
{
// Creates a new thread to run the posted calls.
_workerThread = new Thread(() =>
{
try
{
while (true)
{
var actionStatePair = _actionStatePairs.Take();
SetSynchronizationContext(this);
actionStatePair.Key?.Invoke(actionStatePair.Value);
}
}
catch (ThreadAbortException)
{
Console.WriteLine($"The thread {_workerThread.ManagedThreadId} of {nameof(SingleNewThreadSynchronizationContext)} was aborted.");
}
});
_workerThread.IsBackground = true;
_workerThread.Start();
}
public override void Post(SendOrPostCallback d, object state)
{
// Queues the posted callbacks to be called in this SynchronizationContext.
_actionStatePairs.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
}
public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException();
}
public override void OperationCompleted()
{
_actionStatePairs.Add(new KeyValuePair<SendOrPostCallback, object>(new SendOrPostCallback(_ => _workerThread.Abort()), null));
_actionStatePairs.CompleteAdding();
}
}
这是一个使用它的演示:
static void SingleNewThreadSynchronizationContextDemo()
{
var synchronizationContext = new SingleNewThreadSynchronizationContext();
// Creates some tasks to test that the whole calls in the tasks (before and after awaiting) will be called in the same thread.
for (int i = 0; i < 20; i++)
Task.Run(async () =>
{
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
// Before yielding, the task will be started in some thread-pool thread.
var threadIdBeforeYield = Thread.CurrentThread.ManagedThreadId;
// We yield to post the rest of the task after await to the SynchronizationContext.
// Other possiblity here is maybe to start the whole Task using a different TaskScheduler.
await Task.Yield();
var threadIdBeforeAwait1 = Thread.CurrentThread.ManagedThreadId;
await Task.Delay(100);
var threadIdBeforeAwait2 = Thread.CurrentThread.ManagedThreadId;
await Task.Delay(100);
Console.WriteLine($"SynchronizationContext: thread Id '{synchronizationContext.ManagedThreadId}' | type '{SynchronizationContext.Current?.GetType()}.'");
Console.WriteLine($"Thread Ids: Before yield '{threadIdBeforeYield}' | Before await1 '{threadIdBeforeAwait1}' | Before await2 '{threadIdBeforeAwait2}' | After last await '{Thread.CurrentThread.ManagedThreadId}'.{Environment.NewLine}");
});
}
static void Main(string[] args)
{
Console.WriteLine($"Entry thread {Thread.CurrentThread.ManagedThreadId}");
SingleNewThreadSynchronizationContextDemo();
Console.WriteLine($"Exit thread {Thread.CurrentThread.ManagedThreadId}");
Console.ReadLine();
}
输出:
Entry thread 1
Exit thread 1
SynchronizationContext: thread Id '5' | type 'SynchronizationContexts.SingleNewThreadSynchronizationContext.'
Thread Ids: Before yield '11' | Before await1 '5' | Before await2 '5' | After last await '5'.
SynchronizationContext: thread Id '5' | type 'SynchronizationContexts.SingleNewThreadSynchronizationContext.'
Thread Ids: Before yield '4' | Before await1 '5' | Before await2 '5' | After last await '5'.
SynchronizationContext: thread Id '5' | type 'SynchronizationContexts.SingleNewThreadSynchronizationContext.'
Thread Ids: Before yield '12' | Before await1 '5' | Before await2 '5' | After last await '5'.
SynchronizationContext: thread Id '5' | type 'SynchronizationContexts.SingleNewThreadSynchronizationContext.'
Thread Ids: Before yield '6' | Before await1 '5' | Before await2 '5' | After last await '5'.
SynchronizationContext: thread Id '5' | type 'SynchronizationContexts.SingleNewThreadSynchronizationContext.'
Thread Ids: Before yield '10' | Before await1 '5' | Before await2 '5' | After last await '5'.
SynchronizationContext: thread Id '5' | type 'SynchronizationContexts.SingleNewThreadSynchronizationContext.'
Thread Ids: Before yield '7' | Before await1 '5' | Before await2 '5' | After last await '5'.
关于c# - 为什么 awating 后的代码在不同的线程上运行(即使设置了 SynchronizationContext)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62833028/
我是一名优秀的程序员,十分优秀!