Как предотвратить наследование задачи родительским заданием в контексте логического вызова
Я пытаюсь использовать AsyncLocal в качестве замены локального хранилища Thread при использовании методов Task.Run() и Async. У меня проблема в том, что мне нужен код ниже, чтобы напечатать
from t1 t1
from t1 t1
from t2 t2
from t2 t2
Это будет поведение при использовании локального хранилища Thread, но вместо этого я получаю этот вывод.
from t1 t1
from t1 t1
from t2 t1
from t2 t1
Пример кода:
public class ClientClass {
public static void Main()
{
AsyncLocal<string> _asyncLocalString = new AsyncLocal<string>();
var t1 = Task.Run( async () => {
string a = _asyncLocalString.Value;
if (a == null) {
_asyncLocalString.Value = "t1";
}
a = _asyncLocalString.Value;
Console.WriteLine("from t1 " + a);
await Task.Delay(10);
string b = _asyncLocalString.Value;
Console.WriteLine("from t1 " + b);
var t2 = Task.Run( async () => {
string aa = _asyncLocalString.Value;
if (aa == null) {
_asyncLocalString.Value = "t2";
}
aa = _asyncLocalString.Value;
Console.WriteLine("from t2 " + aa);
await Task.Delay(10);
string bb = _asyncLocalString.Value;
Console.WriteLine("from t2 " + bb);
});
await t2;
});
t1.Wait();
}
}
2 ответа
Решение
Вы можете подавить поток до вызова Task.Run и восстановить его после
public class ClientClass {
public static void Main()
{
AsyncLocal<string> _asyncLocalString = new AsyncLocal<string>();
var t1 = Task.Run(async () =>
{
string a = _asyncLocalString.Value;
if (a == null)
{
_asyncLocalString.Value = "t1";
}
a = _asyncLocalString.Value;
Console.WriteLine("from t1 " + a);
await Task.Delay(10);
string b = _asyncLocalString.Value;
Console.WriteLine("from t1 " + b);
ExecutionContext.SuppressFlow();
var t2 = Task.Run(async () =>
{
string aa = _asyncLocalString.Value;
if (aa == null)
{
_asyncLocalString.Value = "t2";
}
aa = _asyncLocalString.Value;
Console.WriteLine("from t2 " + aa);
await Task.Delay(10);
string bb = _asyncLocalString.Value;
Console.WriteLine("from t2 " + bb);
});
ExecutionContext.RestoreFlow();
await t2;
});
t1.Wait();
}
}
дающий
from t1 t1
from t1 t1
from t2 t2
from t2 t2
Единственный способ, которым я знаю, это использование ThreadPool.UnsafeQueueUserWorkItem:
public class ClientClass {
public static void Main()
{
AsyncLocal<string> _asyncLocalString = new AsyncLocal<string>();
var t1 = Task.Run(async () =>
{
string a = _asyncLocalString.Value;
if (a == null)
{
_asyncLocalString.Value = "t1";
}
a = _asyncLocalString.Value;
Console.WriteLine("from t1 " + a);
await Task.Delay(10);
string b = _asyncLocalString.Value;
Console.WriteLine("from t1 " + b);
var tcs = new TaskCompletionSource<bool>();
ThreadPool.UnsafeQueueUserWorkItem(async s =>
{
string aa = _asyncLocalString.Value;
if (aa == null)
{
_asyncLocalString.Value = "t2";
}
aa = _asyncLocalString.Value;
Console.WriteLine("from t2 " + aa);
await Task.Delay(10);
string bb = _asyncLocalString.Value;
Console.WriteLine("from t2 " + bb);
((TaskCompletionSource<bool>)s).SetResult(true);
}, tcs);
await tcs.Task;
});
t1.Wait();
}
}