How to maintain Thread context across async await model in C#? - c#

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:
Passing it as an argument as #PauloMorgado suggested. Equivalently, you could set it as a field member of an object (it's implicitly passed as an argument via this); or you could have your lambdas capture the variable (underneath, the compiler will implicitly pass it as an argument via this).
Use HttpContext.Items (if you are on ASP.NET 4.5).
Use 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).
Force all 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://learn.microsoft.com/en-us/dotnet/api/system.threading.asynclocal-1?view=netcore-3.1
This works with "async/await" and also with:
Task.Run(...)
Dispatcher.BeginInvoke(...)
new Thread(...).Start()
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

Related

Calling one non thread-safe method in a thread from another thread

Suppose I have a non thread-safe Class X on the main thread and I have another class Y which is in another thread and needs to call a method doX() of Class X.
I would simply pass a reference of Class X to Class Y and call doX() from Y however this class X is non thread-safe and if called from another thread behaves weirdly.
How can I let Y call method doX() of X from X's thread? in the SSCC below the managedthreadid should always be the same (but it isn't).
using System;
using System.Threading;
namespace ThreadApp
{
static class Program
{
[STAThread]
static void Main()
{
int managedThreadId = Thread.CurrentThread.ManagedThreadId;
System.Diagnostics.Debug.WriteLine("Main ManagedThreadId = " + managedThreadId);
X x = new X();
x.doX();
Y y = new Y();
y.fun(x);
}
}
class X
{
public void doX()
{
int managedThreadId = Thread.CurrentThread.ManagedThreadId;
System.Diagnostics.Debug.WriteLine("X ManagedThreadId = " + managedThreadId);
}
}
class Y
{
public void fun(X x)
{
Thread t = new Thread(x.doX);
t.Start();
}
}
}
EDIT: This page explains my problem better than I can: http://mikehadlow.blogspot.it/2012/11/using-blockingcollection-to-communicate.html
Consider these (somewhat) common programming challenges:
I’m using a third party library that is not thread safe, but I want my
application to share work between multiple threads. How do I marshal
calls between my multi-threaded code to the single threaded library? I
have a single source of events on a single thread, but I want to share
the work between a pool of multiple threads? I have multiple threads
emitting events, but I want to consume them on a single thread? One
way of doing this would be to have some shared state, a field or a
property on a static class, and wrap locks around it so that multiple
threads can access it safely. This is a pretty common way of trying to
skin this particular cat, but it’s shot through with traps for the
unwary. Also, it can hurt performance because access to the shared
resource is serialized, even though the things accessing it are
running in parallel.
A better way is to use a BlockingCollection and have your threads
communicate via message classes.
Here's a working solution based on that website suggestion of using BlockingCollection:
namespace ThreadApp
{
static class Program
{
[STAThread]
static void Main()
{
int managedThreadId = Thread.CurrentThread.ManagedThreadId;
System.Diagnostics.Debug.WriteLine("Main ManagedThreadId = " + managedThreadId);
X x = new X();
Y y = new Y();
y.fun(x);
x.doX();
}
}
class X
{
private BlockingCollection<String> queue = new BlockingCollection<String>();
public void Produce(String item)
{
queue.Add(item);
}
public void doX()
{
while (true)
{
String item = queue.Take();
int managedThreadId = Thread.CurrentThread.ManagedThreadId;
System.Diagnostics.Debug.WriteLine("X ManagedThreadId = " + managedThreadId + " randomid=" + item);
// Add your code to process the item here.
// Do not start another task or thread.
}
}
}
class Y
{
X x;
public void fun(X x)
{
this.x = x;
Thread t = new Thread(threadBody);
t.Start();
}
void threadBody()
{
while (true)
{
int managedThreadId = Thread.CurrentThread.ManagedThreadId;
Random rand = new Random();
int randInt = rand.Next(1, 90);
System.Diagnostics.Debug.WriteLine("Y ManagedThreadId = " + managedThreadId + " random-int" + randInt);
x.Produce("random-int" + randInt);
Thread.Sleep(randInt * 10);
}
}
}
}
The above solution works, here's the output:
Main ManagedThreadId = 1
Y ManagedThreadId = 3 random-int24
X ManagedThreadId = 1 randomid=random-int24
Y ManagedThreadId = 3 random-int46
X ManagedThreadId = 1 randomid=random-int46
Y ManagedThreadId = 3 random-int48
X ManagedThreadId = 1 randomid=random-int48
The Y thread inserts a random-int and the X thread receives it in the queue and executes its method in the same thread as the Main thread.
However the problem is the doX() method is inside a while loop so it is blocking. If I have an X class which has some other functions to do and cannot block looping inside a method this approach would not work...
Here's an awesome approach. Use Microsoft's Reactive Framework (Rx).
Rx primarily provides an observable/observer model that is extremely powerful, but it also provides a set of schedulers that can be used to simply work with threads. The EventLoopScheduler scheduler can be used to ensure that code runs on a single thread.
Try this example:
var els = new System.Reactive.Concurrency.EventLoopScheduler();
Console.WriteLine("A" + Thread.CurrentThread.ManagedThreadId);
els.Schedule(() =>
{
Console.WriteLine("B" + Thread.CurrentThread.ManagedThreadId);
});
var thread = new Thread((ThreadStart)(() =>
{
Console.WriteLine("C" + Thread.CurrentThread.ManagedThreadId);
els.Schedule(() =>
{
Console.WriteLine("D" + Thread.CurrentThread.ManagedThreadId);
});
}));
thread.Start();
It outputs:
A12
B14
C16
D14
Both "B" and "D" run on the same thread even though the call to schedule an action came from two different threads.
You can use an EventLoopScheduler to make sure you code on X runs on the same thread.
Just NuGet "System.Reactive" to get the bits.

Parallel.Invoke and thread invocation - Clarification?

I have this code which creates a deadlock :
void Main()
{
ClassTest test = new ClassTest();
lock(test)
{
Task t1 = new Task(() => test.DoWorkUsingThisLock(1));
t1.Start();
t1.Wait();
}
}
public class ClassTest
{
public void DoWorkUsingThisLock(int i)
{
Console.WriteLine("Before " + i);
Console.WriteLine ("Current Thread ID is = "+Thread.CurrentThread.ManagedThreadId);
lock(this)
{
Console.WriteLine("Work " + i);
Thread.Sleep(1000);
}
Console.WriteLine("Done " + i);
}
}
Result :
Before 1
(and deadlock....)
I know that this is a bad practice to lock over instances beyond code's control or , this. But it's just for this question.
I can understand why a deadlock is created here.
The main thread acquires the lock(test) in main and then a new thread starts to invoke DoWorkUsingThisLock - there it tries to acquire a lock over the same instance variable and it's stuck ( because of t1.Wait() at main)
OK
But I've seen this answer here which also creates deadlock.
void Main()
{
ClassTest test = new ClassTest();
lock(test)
{
Parallel.Invoke (
() => test.DoWorkUsingThisLock(1),
() => test.DoWorkUsingThisLock(2)
);
}
}
public class ClassTest
{
public void DoWorkUsingThisLock(int i)
{
Console.WriteLine("Before ClassTest.DoWorkUsingThisLock " + i);
lock(this)
{
Console.WriteLine("ClassTest.DoWorkUsingThisLock " + i);
Thread.Sleep(1000);
}
Console.WriteLine("ClassTest.DoWorkUsingThisLock Done " + i);
}
}
The result is :
Before ClassTest.DoWorkUsingThisLock 1
Before ClassTest.DoWorkUsingThisLock 2
ClassTest.DoWorkUsingThisLock 1 // <---- how ?
ClassTest.DoWorkUsingThisLock Done 1
Question:
How come it DID acquire the lock for the first invocation (DoWorkUsingThisLock(1))? The lock at main is still blocked due to Parallel.Invoke which DOES block !
I don't understand how the thread has succeeded to enter the lock(this) section.
The Parallel class uses the current thread to do a part of the work. This is a nice performance optimization but it is observable in the case of thread-specific state.
The TPL has this kind of "inline execution" in many places and it causes a lot of trouble in different ways. Many programs are not made to deal with reentrancy.

how to get return value when a thread is done

I am trying to do a long process with multi threads. How can I get resutn value from threads? If I can get return values, I will update database according to the return value.
here is my code that calls the threads...
foreach (var obj in elements) {
string body_ = #"<html><head></head><body><a href='http://localhost:5111/Default.aspx?id=" + obj.ID + #"&answer=yes'>Evet</a> - <a href='http://localhost:5111/Default.aspx?id=" + obj.ID + #"&answer=no'>Hayır</a></body></html>";
Thread thread = new Thread(() => sendEmailThread(obj.ALICI, obj.KONU, body_));
thread.Start();
}
here is the thread invoker.....
private void sendEmailThread(string ALICI, string KONU, string body_)
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate() { sendEmail(ALICI, KONU, body_); } );
}
here is the real email sender
public int sendEmail(string to_,string subject_,string body_) {
.......
.......
.......
return 1;
}
Instead of using thread you could using Task which offers many advantages over creating yourself the threads
Your code can be changed to something like this:
var task = Task<int>.Factory.StartNew(
() => sendEmailThread(obj.ALICI, obj.KONU, body_));
task.ContinueWith(() => {var result = task.Result;});
Update
To get a behavior similar to BackgroundWorker you should synchronize your task with the current UI thread using the TaskScheduler.FromCurrentSynchronizationContext()
so the code above can be written like this :
var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();
task.ContinueWith(() => { var result = task.Result; }, UISyncContext);
This article illustrate why you should use Task.Run over BackgroundWorker
In this situation you could just put your code at the end of sendEmail, or as part of the delegation to create the thread like:
private void sendEmailThread(string ALICI, string KONU, string body_)
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate() {
var result = sendEmail(ALICI, KONU, body_);
SomeDBUpdate(result);
} );
}
Even better might be put an eventhandler into your code called something like "OnEmailSendComplete" and pass the return type into the handler.
That said, it's neater to use the new Async methods if the above is all your code is doing rather than writing your own thread handling.
In .NET exists BackgroundWorker class which is the right way to handle time-consuming tasks in background. Try something like this:
foreach (var obj in elements) {
BackgroundWorker bw = new BackgroundWorker();
string body_ = #"<html><head></head><body><a href='http://localhost:5111/Default.aspx?id=" + obj.ID + #"&answer=yes'>Evet</a> - <a href='http://localhost:5111/Default.aspx?id=" + obj.ID + #"&answer=no'>Hayır</a></body></html>";
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync(-- your params here --)
}
You need to create DoWork and RunWorkerCompleted handlers, but I'm sure you can find a lot of tutorials over the internet, for example:
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
Sending Arguments To Background Worker?

Task continuation on UI thread

Is there a 'standard' way to specify that a task continuation should run on the thread from which the initial task was created?
Currently I have the code below - it is working but keeping track of the dispatcher and creating a second Action seems like unnecessary overhead.
dispatcher = Dispatcher.CurrentDispatcher;
Task task = Task.Factory.StartNew(() =>
{
DoLongRunningWork();
});
Task UITask= task.ContinueWith(() =>
{
dispatcher.Invoke(new Action(() =>
{
this.TextBlock1.Text = "Complete";
}
});
Call the continuation with TaskScheduler.FromCurrentSynchronizationContext():
Task UITask= task.ContinueWith(() =>
{
this.TextBlock1.Text = "Complete";
}, TaskScheduler.FromCurrentSynchronizationContext());
This is suitable only if the current execution context is on the UI thread.
With async you just do:
await Task.Run(() => do some stuff);
// continue doing stuff on the same context as before.
// while it is the default it is nice to be explicit about it with:
await Task.Run(() => do some stuff).ConfigureAwait(true);
However:
await Task.Run(() => do some stuff).ConfigureAwait(false);
// continue doing stuff on the same thread as the task finished on.
If you have a return value you need to send to the UI you can use the generic version like this:
This is being called from an MVVM ViewModel in my case.
var updateManifest = Task<ShippingManifest>.Run(() =>
{
Thread.Sleep(5000); // prove it's really working!
// GenerateManifest calls service and returns 'ShippingManifest' object
return GenerateManifest();
})
.ContinueWith(manifest =>
{
// MVVM property
this.ShippingManifest = manifest.Result;
// or if you are not using MVVM...
// txtShippingManifest.Text = manifest.Result.ToString();
System.Diagnostics.Debug.WriteLine("UI manifest updated - " + DateTime.Now);
}, TaskScheduler.FromCurrentSynchronizationContext());
I just wanted to add this version because this is such a useful thread and I think this is a very simple implementation. I have used this multiple times in various types if multithreaded application:
Task.Factory.StartNew(() =>
{
DoLongRunningWork();
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{ txt.Text = "Complete"; }));
});
Got here through google because i was looking for a good way to do things on the ui thread after being inside a Task.Run call - Using the following code you can use await to get back to the UI Thread again.
I hope this helps someone.
public static class UI
{
public static DispatcherAwaiter Thread => new DispatcherAwaiter();
}
public struct DispatcherAwaiter : INotifyCompletion
{
public bool IsCompleted => Application.Current.Dispatcher.CheckAccess();
public void OnCompleted(Action continuation) => Application.Current.Dispatcher.Invoke(continuation);
public void GetResult() { }
public DispatcherAwaiter GetAwaiter()
{
return this;
}
}
Usage:
... code which is executed on the background thread...
await UI.Thread;
... code which will be run in the application dispatcher (ui thread) ...

Synchronous/Blocking Application.Invoke() for GTK#

Unfortunately, Application.Invoke() is asynchronous:
private string ThreadFunction(int i)
{
string result = null;
Gtk.Application.Invoke(delegate
{
OutputStringToUserInterface("i = " + i.ToString());
result = GetStringFromUserInterface();
});
return result;
}
This means that in this example ThreadFunction() proceeds immediately after calling Application.Invoke(), resulting in an probably undefined state of the result string. -- Typically ThreadFunction() will be faster and will return with the old value (i.e. null).
This is a workaround using ManualResetEvent to make Application.Invoke() synchronous:
private string ThreadFunction(int i)
{
string result = null;
using (var ev = new ManualResetEvent(false))
{
Gtk.Application.Invoke(delegate
{
OutputStringToUserInterface("i = " + i.ToString());
result = GetStringFromUserInterface();
ev.Set();
});
ev.WaitOne();
}
return result;
}
This way, ThreadFunction() waits until Application.Invoke() returns, like it would do using WinForms Control.Invoke().
EDIT: Better example code
EDIT2: Add missing using
Now my question: Is there a better solution?
Well, yes, there's no reason to wait for the delegate to execute to get the proper return value. Fix:
int result = i + 1;
And it's okay to let the OutputStringToUserInterface() execute asynchronously, assuming you don't call ThreadFunction() so often that it floods the UI thread with requests.
If your real code actually depends on a return value from a function that must run on the UI thread then, no, you can't make it faster. Clearly, that's something you really want to avoid.
You can encapsulate your current code into a generic wrapper:
public static void GuiInvoke(Action action)
{
var waitHandle = new ManualResetEventSlim();
Gtk.Application.Invoke( (s,a) =>
{
action();
waitHandle.Set();
});
waitHandle.Wait();
}
public static void BeginGuiInvoke(Action action)
{
Gtk.Application.Invoke( (s,a) => {action();});
}

Categories

Resources