The above article talks about how a string of callbacks is a concurrent process, and obviously writing code this way, adding logic and inserting logic is very error prone. We need to use asynchronous syntax to change the form of this asynchronous callback to a synchronous form, fortunately C# has been designed for us, see the code
// example2_2
class Program
{
private static int loopCount = 0;
static void Main(string[] args)
{
OneThreadSynchronizationContext _ = OneThreadSynchronizationContext.Instance;
Console.WriteLine($"Main Thread: {Thread.CurrentThread.ManagedThreadId}");
Crontine();
while (true)
{
OneThreadSynchronizationContext.Instance.Update();
Thread.Sleep(1);
++loopCount;
if (loopCount % 10000 == 0)
{
Console.WriteLine($"loop count: {loopCount}");
}
}
}
private static async void Crontine()
{
await WaitTimeAsync(5000);
Console.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount's value is: {loopCount}");
await WaitTimeAsync(4000);
Console.WriteLine($"Current Thread: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih The value of loopCount is: {loopCount}");
await WaitTimeAsync(3000);
Console.WriteLine($"Current Thread: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih The value of loopCount is: {loopCount}");
}
private static Task WaitTimeAsync(int waitTime)
{
TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
Thread thread = new Thread(()=>WaitTime(waitTime, tcs));
thread.Start();
return tcs.Task;
}
/// <summary>
/// Waiting in another thread
/// </summary>
private static void WaitTime(int waitTime, TaskCompletionSource<bool> tcs)
{
Thread.Sleep(waitTime);
// throw tcs back to the main thread for execution
OneThreadSynchronizationContext.Instance.Post(o=>tcs.SetResult(true), null);
}
}
In this code, in the WaitTimeAsync method, we use the TaskCompletionSource class instead of the previously passed Action parameter, and the WaitTimeAsync method returns a Task type result. waitTime we replace action() with tcs. SetResult(true), and the WaitTimeAsync method uses the await keyword in front of it, so that the sequence of callbacks can be changed to a synchronized form. This makes the code look very simple and much easier to develop.
Here is another trick, we found that WaitTime needs to throw tcs.SetResult back to the main thread for execution, Microsoft gives us a simple way to set up the synchronization context in the main thread by referring to example2_2_2
// example2_2_2
SynchronizationContext.SetSynchronizationContext(OneThreadSynchronizationContext.Instance);
SetResult(true) will be called directly in WaitTime, the callback will be automatically thrown to the synchronization context, and the synchronization context we can take out in the main thread to execute the callback, so automatically able to complete the operation back to the main thread
private static void WaitTime(int waitTime, TaskCompletionSource<bool> tcs)
{
Thread.Sleep(waitTime);
tcs.SetResult(true);
}
If you do not set the synchronization context, you will find that the printout of the current thread is not the main thread, which is also the use of many third-party libraries and . In fact, I think this design is not necessary, to the library developers to achieve better, especially in the game development, logic is all single-threaded, callback every time you go through the synchronization context is redundant, so the ET framework provides the implementation of ETTask does not use the synchronization context, the code is more concise and efficient, which will be discussed later.