Skip to content

Latest commit

 

History

History
119 lines (101 loc) · 6.02 KB

2.1CSharp Coroutine.md

File metadata and controls

119 lines (101 loc) · 6.02 KB

What is a concurrent thread

Speaking of concurrency, let's first understand what is asynchronous. Asynchronous simply means that I want to initiate a call, but the called party (may be other threads, or may be IO) will take some time to produce the result, and I don't want the call to block the entire thread of the caller, so I pass a callback function to the called party, and the called party will call back this callback function after it finishes running to notify the caller to continue The callback function will notify the caller to continue execution. As an example:
In the following code, the main thread keeps looping, sleep 1 millisecond per loop, add one to the count, and print once every 10,000 times.

        private static void Main()
        {
            int loopCount = 0;
            while (true)
            {
                int temp = watcherValue;
                
                Thread.Sleep(1);
                
                ++loopCount;
                if (loopCount % 10000 == 0)
                {
                    Console.WriteLine($"loop count: {loopCount}");
                }
            }
        }

At this point I need to add a function, at the beginning of the program, I want to print the value of loopCount after 5 seconds. Seeing that after 5 seconds we can think of the Sleep method, which will block the thread for a certain amount of time and then continue execution. We obviously can't Sleep in the main thread because it would break the logic of printing once every 10000 counts.

    // example2_1
    class Program
    {
        private static int loopCount = 0;

        private static void Main()
        {
            OneThreadSynchronizationContext _ = OneThreadSynchronizationContext.Instance;
            
            WaitTimeAsync(5000, WaitTimeFinishCallback);
            
            while (true)
            {
                OneThreadSynchronizationContext.Instance.Update();
                
                Thread.Sleep(1);
                
                ++loopCount;
                if (loopCount % 10000 == 0)
                {
                    Console.WriteLine($"loop count: {loopCount}");
                }
            }
        }

        private static void WaitTimeAsync(int waitTime, Action action)
        {
            Thread thread = new Thread(()=>WaitTime(waitTime, action));
            thread.Start();
        }
        
        private static void WaitTimeFinishCallback()
        {
            Console.WriteLine($"WaitTimeAsync finsih loopCount value is: {loopCount}");
        }

        /// <summary>
        /// Waiting in another thread
        /// </summary>
        private static void WaitTime(int waitTime, Action action)
        {
            Thread.Sleep(waitTime);
            
            // Throw the action back to the main thread for execution
            OneThreadSynchronizationContext.Instance.Post((o)=>action(), null);
        }
    }

We have designed a WaitTimeAsync method here. WaitTimeAsync is actually a typical asynchronous method that initiates a call from the main thread, passes in a WaitTimeFinishCallback method as an argument, opens a thread, and after the thread Sleeps for a certain amount of time, throws the passed callback back to the main thread for execution. OneThreadSynchronizationContext is a cross-thread queue, any thread can throw delegates into it, the Update method of OneThreadSynchronizationContext is called in the main thread and will take out these delegates and put them into the main thread for execution. Why does the callback method need to be thrown back to the main thread for execution? Because the loopCount is read in the callback method, and the loopCount is also read and written in the main thread, so either add a lock or always ensure that it is only read and written in the main thread. It is a bad practice to add locks, and having locks all over the code makes it difficult to read and maintain, and it is easy to create multi-threaded bugs. this is a common technique in multi-threaded development to package the logic into a delegate and throw it back to another thread.

We may have a new requirement, after the execution of WaitTimeFinishCallback is finished, we want to wait for 3 seconds and print the loopCount again.

        private static void WaitTimeAsync(int waitTime, Action action)
        {
            Thread thread = new Thread(()=>WaitTime(waitTime, action));
            thread.Start();
        }
        private static void WaitTimeFinishCallback()
        {
            Console.WriteLine($"WaitTimeAsync finsih loopCount value is: {loopCount}");
            WaitTimeAsync(3000, WaitTimeFinishCallback2);
        }
        
        private static void WaitTimeFinishCallback2()
        {
            Console.WriteLine($"WaitTimeAsync finsih loopCount value is: {loopCount}");
        }

We may also change the requirement at this point, we need to print the loopCount 5 seconds after the program starts, then 4 seconds after, then 3 seconds after, that is, insert another 3 seconds wait in the middle of the above logic.

        private static void WaitTimeAsync(int waitTime, Action action)
        {
            Thread thread = new Thread(()=>WaitTime(waitTime, action));
            thread.Start();
        }
        
        private static void WaitTimeFinishCallback()
        {
            Console.WriteLine($"WaitTimeAsync finsih loopCount value is: {loopCount}");
            WaitTimeAsync(4000, WaitTimeFinishCallback3);
        }
        
        private static void WaitTimeFinishCallback3()
        {
            Console.WriteLine($"WaitTimeAsync finsih loopCount value is: {loopCount}");
            WaitTimeAsync(3000, WaitTimeFinishCallback2);
        }
        
        private static void WaitTimeFinishCallback2()
        {
            Console.WriteLine($"WaitTimeAsync finsih loopCount value is: {loopCount}");
        }

This inserts a piece of code in the middle, which seems very cumbersome. Here you can answer what is a concurrent process, in fact this string of callbacks is a concurrent process.