Is using ThreadStatic and setting the context every time await completes "an option"? Is there another way?
public async void Test()
{
// This is in Thread 1
Foo foo = new Foo();
Context.context = "context1"; // This is ThreadStatic
string result = await foo.CallAsynx();
// This is most likely Thread 2
Context.context = "context1"; // This might be a different thread and so resetting context
}
Now is there another way if I don't want to use ThreadStatic?
ThreadStatic
, ThreadLocal<T>
, thread data slots, and CallContext.GetData
/ CallContext.SetData
do not work well with async
, since they are thread-specific.
The best alternatives are:
this
); or you could have your lambdas capture the variable (underneath, the compiler will implicitly pass it as an argument via this
).HttpContext.Items
(if you are on ASP.NET 4.5).CallContext.LogicalGetData
/ CallContext.LogicalSetData
as @Noseratio suggested. You can only store immutable data in the logical thread context; and it only works on .NET 4.5 and is not available on all platforms (e.g., Win8).async
continuations back to the same thread by installing a "main loop" for that thread, such as the AsyncContext
from my AsyncEx library.Just if someone has the same question some years later and finds this thread...
There is a new feature called
AsyncLocal<T>
https://docs.microsoft.com/en-us/dotnet/api/system.threading.asynclocal-1?view=netcore-3.1
This works with "async/await" and also with:
I just testet those three with the following code:
private void StartTests() {
Thread.Sleep(1000);
Task.Run(() => DoWork1());
Task.Run(() => DoWork2());
}
private void DoWork1() {
ThreadContext.Context.Value = "Work 1";
Thread.Sleep(5);
Task.Run(() => PrintContext("1"));
Thread.Sleep(10);
Dispatcher.BeginInvoke(new Action(() => PrintContext("1")));
Thread.Sleep(15);
var t = new Thread(() => PrintContextT("1"));
t.Start();
}
private void DoWork2() {
ThreadContext.Context.Value = "Work 2";
Task.Run(() => PrintContext("2"));
Thread.Sleep(10);
Dispatcher.BeginInvoke(new Action(() => PrintContext("2")));
Thread.Sleep(10);
var t = new Thread(() => PrintContextT("2"));
t.Start();
}
private void PrintContext(string c) {
var context = ThreadContext.Context.Value;
Console.WriteLine("P: " + context + "-" + c);
Task.Run(() => PrintContext2(c));
}
private void PrintContext2(string c) {
Thread.Sleep(7);
var context = ThreadContext.Context.Value;
Console.WriteLine("P2: " + context + "-" + c);
}
private void PrintContextT(string c) {
var context = ThreadContext.Context.Value;
Console.WriteLine("T: " + context + "-" + c);
}
public class ThreadContext {
public static AsyncLocal<object> Context = new AsyncLocal<object>();
}
Output:
P: Work 2-2
P: Work 1-1
P2: Work 2-2
P: Work 2-2
P2: Work 1-1
P: Work 1-1
P2: Work 2-2
T: Work 2-2
P2: Work 1-1
T: Work 1-1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With