Code switching in same thread

  • Thread starter Thread starter Heiko65456465
  • Start date Start date
H

Heiko65456465

Guest
Hi,

I have two async methods, which are started one after the other in the same (GUI) thread by button clicks. The first method creates a TaskCompletionSource and executes an asynchronous processing. Meanwhile, the second method is started that waits for the TaskCompletionSource task. If TaskCompletionSource.SetResult() is then called in the first method, the remaining code in the second method is executed immediately before the code of the first method is continued.

public sealed partial class MainPage : Page
{
TaskCompletionSource<object> Tcs;

public MainPage()
{
this.InitializeComponent();
}

private async void Button1_Click(object sender, RoutedEventArgs e)
{
Trace("1 Button clicked, ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId);

Tcs = new TaskCompletionSource<object>();
Trace("1 TaskCompletionSource created");

Trace("1 working...");
await Task.Delay(10_000);

Trace("1 before SetResult()");
Tcs.SetResult(null);

Tcs = null;
Trace("1 Ended");
}

private async void Button2_Click(object sender, RoutedEventArgs e)
{
Trace("2 Button clicked, ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId);

if (Tcs != null)
{
Trace("2 waiting...");
await Tcs.Task;
}
Trace("2 Ended");
}

private void Trace(string text)
{
Debug.WriteLine(text);
}
}



Output:

1 Button clicked, ManagedThreadId: 3
1 TaskCompletionSource created
1 working...
2 Button clicked, ManagedThreadId: 3
2 waiting...
1 before SetResult()
2 Ended
1 Ended



I suppose that's a defined behavior. I have created a CriticalSection class based on TaskCompletionSource that allows an awaitable lock. When using several threads it works fine. However, it also works when there are multiple parallel uses within the same thread, without blocking execution, as would be the case with lock. It is used as follows:

CriticalSection MyLock = new CriticalSection();

private async void MethodA()
{
// ...
using (CriticalSectionRun runA = await MyLock.EnterAsync())
{
// ...
}
DoSmthA();
}

private async void MethodB()
{
// ...
using (CriticalSectionRun runB = await MyLock.EnterAsync())
{
// ...
}
DoSmthB();
}


If runA.Dispose() is called when leaving the using scope, TaskCompletionSource.SetResult() is called internally. MethodB() then immediately receives a valid CriticalSectionRun object and can execute its critical code. If MethodA() and MethodB() are used in different threads, everything is fine.

However, if MethodA() and MethodB() are called in the same thread, it does not appear at first glance why after runA.Dispose() DoSmthB() is executed and not DoSmthA(), although they run in the same thread.

As I mentioned, I think it's defined behavior. However, I have the question: Do I have any possibility to execute the code after TaskCompletionSource.SetResult() first after calling it?


Best Regards,

Heiko

Continue reading...
 
Back
Top