C # – calling asynchronous methods from non asynchronous methods

summary

Each variation of the following code I tried didn't work – either DoSomething(): void or being called write or DoSomething(): Task and using taskex Runex() call, some attempts involve GetAwaiter(). Call getResult() Errors seen include: "Start may not be called on a Task with a null operation", "RunSynchronously may not be called on a Task that is not bound to a delegate", and "the Task has not been completed"

class Program
{
    static void Main(string[] args) // Starting from a non-async method
    {
        DoSomething();
 
        Console.WriteLine("Press any key to quit.");
        Console.ReadKey();
    }
 
    static async void DoSomething()
    {
        Console.WriteLine("Starting DoSomething ...");
 
        var x = await PrepareAwaitable(1);
 
        Console.WriteLine("::" + x);
 
        var y = await PrepareAwaitable(2);
 
        Console.WriteLine("::" + y);
    }
 
    static Task<string> PrepareAwaitable(int id)
    {
        return new Task<string>(() =>
        {
            return "Howdy " + id.ToString();
        });
    }
}

Output:

Starting DoSomething ...

Press any key to quit.

Preparewaitable tasks and actions will be more complex in the future When this action is completed, no matter how long, I want to restore the task (or other framework mechanism) by assigning "Howdy..." to X and then to y What I really want to do is intercept the waiting objects, process them, and resume the continuation of the results (x and y) at a later time under my control But I didn't go too far in this important step, so I tried to start small

resolvent

Your return mission has not yet started (i.e., they are "cold" missions); Try replacing the preparewaitable code with the following:

static Task<string> PrepareAwaitable(int x)
{
    return Task.Factory.StartNew<string>(() =>
    {
        return "Howdy " + x.ToString();
    });
}

C # simple multithreading and calling synchronous methods asynchronously


Viewing MSDN, we can see that the Thread class has four constructors and two main parameters: ThreadStart and ParameterizedThreadStart;

You can see from MSDN:

1. ThreadStart is a delegate type with no parameters and no return value;

2. ParameterizedThreadStart is a delegate type with an object type parameter and no return value;

As shown below:

Source code of use case:

  class Program
    {        
        public static void Main(string[] args)
        {
            ThreadStart method = null;
            Thread withoutParam = null;
            //1-1 no parameter - > threadstart object initializes thread object
            method = Sum2;//Method = new threadstart (sum2) method + = sum2 (multicast delegation)
            withoutParam = new Thread(method);
            withoutParam.Name = "No parameters->ThreadStart Object initializes the thread object";
            withoutParam.Start();
 
            //1-2 no parameter - > function initialization thread object
            withoutParam = new Thread(Sum2) { IsBackground = true, Name = "No parameters->Function initializes the thread object" };
            withoutParam.Start();
 
            //1-3 no parameter - > anonymous function initialization thread object
            withoutParam = new Thread(delegate()
            {
                for (int i = 0; i < 5; i++)
                {
                    if (i == 3)
                        Thread.CurrentThread.Abort();
                    Thread.Sleep(50);
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "-->" + Thread.CurrentThread.Name);
                }
            }) { IsBackground = true, Name = "No parameters->Initializing Thread objects with anonymous functions" };
            withoutParam.Start();
 
            //1-4 no parameters - > lambda expression initializing thread object
            withoutParam = new Thread(() =>
            {
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(50);
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "-->" + Thread.CurrentThread.Name);
                }
            },1) { IsBackground = true, Name = "Lambda Expression initializing thread object" };
            withoutParam.Start();
 
 
            ParameterizedThreadStart method2;
            Thread whitParam = null;
            //Threadstart object initialization with threadstart - > 2
            method2 = Sum3;
            whitParam = new Thread(method2);
            whitParam.Name = "**********With parameters**********->->ParameterizedThreadStart Object initializes the thread object";
            whitParam.Start(2);
            //2-2 thread object with parameter - > function initialization
            whitParam = new Thread(Sum3) { Name = "**********With parameters**********->->Function initializes the thread object" };
            whitParam.Start(3);
 
            //2-3 thread object initialized with parameter - > anonymous method
            whitParam = new Thread(delegate(object para){
                int count = (int)para;
 
                for (int i = 0; i < count; i++)
                {
                    Thread.Sleep(50);
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":::" + Thread.CurrentThread.Name);
                }
 
            }) { Name = "**********With parameters**********->Initializing Thread objects with anonymous functions" };
            whitParam.Start(1);
 
            //2-4 initializing thread object with parameter - > lambda expression
            whitParam = new Thread((para)=>
            {
                int count = (int)para;
 
                for (int i = 0; i < count; i++)
                {
                    Thread.Sleep(50);
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":::" + Thread.CurrentThread.Name);
                }
 
            }) { Name = "**********With parameters**********->Lambda Expression initializing thread object" };
            whitParam.Start(100);
 
 
            Console.ReadKey();
        }
     
        private static void Sum2()
        {
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(50);
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "-->" + Thread.CurrentThread.Name);
            }
        }
 
        private static void Sum3(object para)
        {
            int count = (int)para;
 
            for (int i = 0; i <count ; i++)
            {
                Thread.Sleep(50);
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId +":::"+ Thread.CurrentThread.Name);
            }
        }
 
    }

In this way, what if the result of thread processing is required?

1. Define a thread parameter class code as follows:

/// <summary>
    ///Thread parameters
    /// </summary>
    class ThreadParam
    {
        /// <summary>
        ///Thread processing parameters
        /// </summary>
        public int X;
 
        /// <summary>
        ///Thread processing parameters
        /// </summary>
        public int Y;
       
        /// <summary>
        ///Action is a generic delegate without return value, which is used for thread processing result callback
        /// </summary>
        public Action<int> CallBack;
    }

2. Complete example:

 class Program
    {               
 
        public static void Main(string[] args)
        {
            Console.WriteLine("Program execution started:*********" );                 
            Thread whitParam = new Thread(Sum3);
            ThreadParam param = new ThreadParam() { X = 10, Y = 20 };
            param.CallBack = ThreadCallBack;
            whitParam.Start(param);
            for (int i = 0; i < 20; i++)
            {
                Thread.Sleep(5);
                Console.WriteLine("Main thread ID: "+Thread.CurrentThread.ManagedThreadId);
            }
            Console.ReadKey();
        }
 
        private static void ThreadCallBack(int result)
        {
            Thread.Sleep(10);
            Console.WriteLine("Thread processing result:"+result);
        }
 
        private static void Sum3(object para)
        {           
            ThreadParam p = para as ThreadParam;
            int rsult = p.X + p.Y;
            if (p.CallBack != null)
            {
                p.CallBack(rsult);  
            }
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "-->Thread end");
        }
 
    }

2, Call synchronous methods asynchronously:

use. NET Framework can call any method asynchronously. To implement this operation, you must define a delegate with the same signature as the method you want to call; The common language runtime automatically defines BeginInvoke and EndInvoke methods for this delegate with the appropriate signature.

A. The BeginInvoke method starts the asynchronous call and returns IAsyncResult, which can obtain the completion progress of the asynchronous call.

B. The EndInvoke method obtains the asynchronous call result and calls it anywhere after the BeginInvoke method. If the asynchronous call method is not completed, the EndInvoke method will block the calling thread until the asynchronous call is completed.

1. There are four ways to make asynchronous calls using BeginInvoke method and EndInvoke method:

A, perform some operations, and then call EndInvoke to stop until the completion of the call; The code is as follows:

 public static void Main(string[] args)
        {
            //Declare a generic delegate (the return value is int type, with 2 int type parameters);
            //Assign values to delegates using lambda expressions. The expression sleep for 1 second, and then returns the sum of two numbers;
            Func<int, int, int> testAction = (x, y) => { Thread.Sleep(1000); return x + y; };
          
            IAsyncResult result = testAction.BeginInvoke(10, 15, null, null);
            //Some other operations can be performed between BeginInvoke and EndInvoke methods
 
            int sum = testAction.EndInvoke(result);//If the operation of A does not take 1 second to complete, EndInvoke will block and wait until the result comes back, and the program will execute the code below the EndInvoke method
            Console.WriteLine("Result of asynchronous call:" + sum);
            Console.ReadKey();
        }

B. Use the waithandle property to get IAsyncResult AsyncWaitHandle uses its WaitOne method to stop execution until WaitHandle receives the signal and then calls EndInvoke. The code is as follows:

 public static void Main(string[] args)
        {
            //Declare a generic delegate (the return value is int type, with 2 int type parameters);
            //Define an anonymous method to assign values to delegates. The method sleeps for 1 second, and then returns the sum of two numbers;
            Func<int, int, int> testAction = (x, y) => { Thread.Sleep(1000); return x + y; };
          
            IAsyncResult result = testAction.BeginInvoke(10, 15, null, null);
            //Some other operations can be performed between BeginInvoke and EndInvoke methods         
            result.AsyncWaitHandle.WaitOne();           
            int sum = testAction.EndInvoke(result);//If the operation of A does not take 1 second to complete, EndInvoke will block and wait until the result comes back, and the program will execute the code below the EndInvoke method
            Console.WriteLine("Result of asynchronous call:" + sum);
            result.AsyncWaitHandle.Close();
            Console.ReadKey();
        }

C, polling the IsCompleted returned by BeginInvoke to IAsyncResult to determine the time of completion of the asynchronous call, and then invoke EndInvoke. The code is as follows:

 public static void Main(string[] args)
        {
            //Declare a generic delegate (the return value is int type, with 2 int type parameters);
            //Define an anonymous method to assign values to delegates. The method sleeps for 1 second, and then returns the sum of two numbers;
            Func<int, int, int> testAction = (x, y) => { Thread.Sleep(1000); return x + y; };
          
            IAsyncResult result = testAction.BeginInvoke(10, 15, null, null);
            //Some other operations can be performed between BeginInvoke and EndInvoke methods     
            while (!result.IsCompleted)
            {
                Console.Write("*");
                Thread.Sleep(250);
            }
                    
            int sum = testAction.EndInvoke(result);//If the operation of A does not take 1 second to complete, EndInvoke will block and wait until the result comes back, and the program will execute the code below the EndInvoke method
            Console.WriteLine("Result of asynchronous call:" + sum);          
            Console.ReadKey();
        }

D. Pass the delegate of the callback method to BeginInvoke. After the asynchronous call is completed, execute the testcallback method on the ThreadPool thread. Call EndInvoke in the TestCallBack method. The code is as follows:

 public static void Main(string[] args)
        {
            //Declare a generic delegate (the return value is int type, with 2 int type parameters);
            //Define an anonymous method to assign values to delegates. The method sleeps for 1 second, and then returns the sum of two numbers;
            Func<int, int, int> testAction = (x, y) => { Thread.Sleep(1000); return x + y; };
            //Declare a delegate 
            AsyncCallback callBack =TestCallBack;
            IAsyncResult result = testAction.BeginInvoke(10, 15, callBack, "I am call back param");
          //  IAsyncResult result = testAction.BeginInvoke(10, 15, TestCallBack, "I am call back param");              
            Console.ReadKey();
        }
        public static void TestCallBack(IAsyncResult ar)
        {
            AsyncResult result = (AsyncResult)ar;
            Func<int, int, int> caller = (Func<int, int, int>)result.AsyncDelegate;
            string parameter = (string)ar.AsyncState;
            Console.WriteLine(parameter);
            Console.WriteLine("Processing results of asynchronous methods:" + caller.EndInvoke(ar));
        }

Summary: the calling thread needs to process the asynchronous processing results. The methods that can be used are A, B and C. If the calling thread does not need to process asynchronous results, D mode is used. The CallBack method is invoked in the ThreadPool thread to process asynchronous results.

Keywords: C# Back-end .NET

Added by BillBillJr on Sun, 06 Mar 2022 13:03:00 +0200