Moh*_*din 5 .net c# async-await
我正在执行以下代码片段来测试如何更改线程,在该线程上将await调用 ing 之后的代码。根据@Stephen Cleary 在这个答案中,为了能够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();
}
Run Code Online (Sandbox Code Playgroud)
输出:
你好世界!ThreadId: 1
结束ThreadId: 1
是否有Sync Contex?:True
在延迟之前。ThreadId: 3
延迟后。线程 ID:4
为什么会发生这种情况?
我想根据我的理解展示一些代码,希望它可以帮助别人。
正如 aepot、dymanoid 和 Hans Passant(感谢他们)所说,使用默认值SynchronizationContext只会Post在awaiting 到SynchronizationContext.
我创建了一个非常非常基本但不是最佳的SynchronizationContext来演示基本实现应该是什么样子。我的实现将创建一个新的并在同一个新创建的特定上下文中Thread运行一些s 。TaskThread
更好的实现(但非常复杂)可以在Stephen Cleary 的 GitHub 存储库中找到。
我的实现基本上如下所示(来自我的 GitHub 存储库,存储库中的代码将来可能会有所不同):
/// <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();
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个使用它的演示:
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();
}
Run Code Online (Sandbox Code Playgroud)
输出:
/// <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();
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
350 次 |
| 最近记录: |