You do not need to pass parameters or return parameters.
We know that the most intuitive way to start a thread is to use the Thread class. The specific steps are as follows:
Threadstart = new threadstart (calculate );
Thread thread = new thread (threadstart );
Thread. Start ();
Public void calculate (){
Double diameter = 0.5;
Console. Write ("the area of circle with a diameter of {0} is {1}" diameter, diameter * Math. Pi );
}
We have definedThreadstart type DelegationThis delegate develops the method to be executed by the thread: Calculate, in which the area of a circle with a diameter of 0.5 is calculated and output. this constitutes the simplest example of multithreading. In many cases, this is enough, and the threadstart delegate is defined as void threadstart (), that is, the method to be executed does not have parameters. This is obviously a huge deficiency. To make up for this defect, a smart programmer has come up with many good methods, we will introduce it in the section that needs to pass multiple parameters. Here we will first introduce it. net another delegate set to solve this problem: parameterizedthreadstart, which will be detailed below.
A single parameter needs to be passed
Parameterthreadstart is defined as void parameterizedthreadstart (object state )?? The startup function of the thread defined by this delegate can accept an input parameter. The example is as follows:
Parameterizedthreadstart threadstart = new parameterizedthreadstart (calculate)
Thread thread = new thread ()
Thread. Start (0.9 );
Public void calculate (Object Arg ){
Double diameter = double (ARG );
Console. Write ("the area of circle with a diameter of {0} is {1}" diameter, diameter * Math. Pi );
}
Example 2
The calculate method has a parameter of the object type. Although there is only one parameter and it is of the object type, type conversion is still required when using it, but fortunately there is a parameter, in addition, by combining multiple parameters into a class, and passing the instance of this class as a parameter, multiple parameters can be passed.
Multiple parameters need to be passed
Although multiple parameters can be transferred by entrusting parameterizedthreadstart by encapsulating the required parameters into a class, the parameter conversion is inevitable because the input parameter of this delegate is an object, there are several common parameter passing methods below. Let's take a look at them one by one.
Use special thread classes
This is a classic mode that many programmers love to use. Simply put, they put the methods that need to be executed by another thread and the parameters they need into a class, and the parameters are used as the attributes of the class, declare the instance during the call, and then initialize the attributes. When the method is executed, use the initialized attributes in the class for execution, so that the method itself does not need parameters, the multi-parameter transfer effect is achieved, so we can use the threadstart delegation without parameters mentioned at the beginning of this article, and because the methods and parameters to be executed are put in a class, fully embodies the characteristics of object-oriented. the procedure is as follows:
We use a class to package the method for calculating the area. The input parameter diameter (diameter) is a field of this class.
Public class mythread
{
Public double diameter = 10;
Public double result = 0;
Public mythread (INT diameter)
{
This. Diameter = diameter;
}
Public void calculate ()
{
Console. writeline ("Calculate start ");
Thread. Sleep (2000 );
Result = diameter * Math. Pi ;;
Console. writeline ("calculate end, diameter is {0}, result is {1}", this. diameter, result );
}
}
Mythread T = new mythread (5.0 );
Threadstart = new threadstart (T. Calculate)
Thread thread = new thread (threadstart );
Thread. Start ();
Example 3
In this way, the parameter transfer is changed to attribute sharing. It is good to encapsulate the data involved in the logic and the logic in terms of encapsulation, this method also has a clever variant that uses the anonymous method. This variant can be used to save independent classes. Now I am giving this method.
Double diameter = 6;
Double result = 0;
Thread TA = new thread (New threadstart (delegate ()
{
Thread. Sleep (2000 );
Result = diameter * Math. Pi;
Console. writeline ("anonymous calculate end, diameter is {0}, result is {1}", diameter, result );;
}));
Ta. Start ();
Example 4
This method is the same as in the preceding example. It changes the parameter transfer to the call to the variable, thus canceling the parameter transfer. However, the latter makes full use of a property of the anonymous method, you can directly use the local variables of the current context, suchDelegateOf course, the disadvantage of doing so is that if the anonymous method is too long, the program's readability will be reduced, so few people usually do this. Here we provide this method for your reference.
Smart readers must think that, since fields can be used to input variables, you can also use fields to output variables, for example, in the above two examples, we can see that the calculation results are written into a variable named result (highlighted). Can we directly access this variable to get the calculation result?
In this case, there is a fatal problem: Since it is asynchronous execution, how does the main thread know when the split thread completes the calculation? For example, in the above two examples, our threads are sleeping for 2000 milliseconds before calculation. If the main thread accesses the result before the computation is completed, only one 0 value can be obtained. so we have a series of solutions below.
Parameters need to be passed and return parameters
As mentioned earlier, the main thread needs to know when the sub-thread is executed. You can use the thread. threadstate enumeration to determine when the sub-thread is executed.
When the threadstate of the thread is = threadstate. when you stop the job, it usually means that the thread has finished the job and the result is available. If it is not in this status, continue to execute other jobs, or wait for a while and try again. if we need to wait for multiple subthreads to return data and use their results for asynchronous computing, it is called thread synchronization, next, we will introduce another method that I recommend. It can customize the number of parameters and return data, which is relatively convenient to use.
Use delegate asynchronous call methods and callbacks
First, we need to define the method to be called asynchronously as a delegate, and then use begininvoke for asynchronous call. The first parameter of begininvoke is the diameter, the second is the method called after the thread is executed.
Delegate double calculatemethod (double diameter );
Static calculatemethod calcmethod;
Double result = 0;
Static void main (string [] ARGs)
{
Calcmethod = new calculatemethod (calculate );
Calcmethod. begininvoke (5, new asynccallback (taskfinished), null );
}
///
/// The function called by the thread
///
Public static double calculate (double diameter)
{
Return diameter * Math. Pi;
}
///
/// The callback function after the thread is completed
///
Public static void taskfinished (iasyncresult result)
{
Result = calcmethod. endinvoke (result );
}
Example 5
Note: In the taskfinished method executed after the thread execution is complete, we use endinvoke to obtain the return value of this function.
Thread Pool
Although a thread is a good thing, it is also a large resource consumer. In many cases, we need to use multiple threads, but we do not want too many threads. This is the role of the thread pool ,. NET provides a ready-made thread pool threadpool. Its usage is as follows:
Waitcallback W = new waitcallback (calculate );
Threadpool. queueuserworkitem (W, 1.0 );
Threadpool. queueuserworkitem (W, 2.0 );
Threadpool. queueuserworkitem (W, 3.0 );
Threadpool. queueuserworkitem (W, 4.0 );
Public static void calculate (double diameter)
{
Return diameter * Math. Pi;
}
Example 6
First define a waitcallback delegate. The waitcallback format is void waitcallback (Object State). That is to say, your method must conform to this format. Then call queueuserworkitem to add the task to the thread pool, when the county city pool has a idle line, your code will be scheduled and run.
Every process has a thread pool. The default thread pool size is 25. We can set its maximum value through the setmaxthreads method.
[Note]Since each process has only one thread pool, if the thread pool is used in IIS or sqlserver processes and the maximum capacity of the thread pool needs to be set, it will affect the IIS process or SQL process, so be careful in both cases.
Control
When talking with everyone, I found that all colleagues who are used to object-oriented thinking are always troubled by the execution context in the multi-thread scenario. For example, in Example 5, the main program starts the subthread to execute the calculate method, and calls back taskfinished after execution. If the main thread ID is 1, The subthread ID is 2, so calculate must be executed in the thread id = 2. What about the taskfinished callback function? It is also executed in the context of the thread whose ID is 2. If you do not believe it, try to output the thread ID. This is usually not a problem, but when we need to use the subthread in winform programming, this may cause problems. We will discuss this issue below.
Special features of multi-thread programming for form programs
When we move the callback code in Example 5 to winform, we can see the problem.
Public static void taskfinished (iasyncresult result)
{
Result = calcmethod. endinvoke (result );
This. textbox1.text = result;
}
The purpose of the program is to write a textbox into the result after the thread completes execution. However, when the program runs this. textbox1.text = result. in the past, winform had strict requirements on threads. Apart from the threads used to create these controls, other threads do not allow cross-thread access to the properties and methods of controls on winform (except for a few special attributes). In some versions of the system, such as XP, this issue is handled, cross-thread control access can be executed, but most Windows systems are not. If we do need to modify the control attributes across threads or call the control method, you must use the control method invoke. This method can switch the execution context back to the thread for creating these controls. The specific operations are as follows:
Delegate void changetext (string result );
Public static void taskfinished (iasyncresult result)
{
Result = calcmethod. endinvoke (result );
This. begininvoke (New changetext (this. textbox1.appendtext), T. Result. tostring ())
}
Because methods must be used in the delegate, I use the appendtext method instead of directly setting the text attribute. If you want to set the text attribute, you must encapsulate a method and connect it to the delegate.
You do not need to pass parameters or return parameters.
We know that the most intuitive way to start a thread is to use the Thread class. The specific steps are as follows:
Threadstart = new threadstart (calculate );
Thread thread = new thread (threadstart );
Thread. Start ();
Public void calculate (){
Double diameter = 0.5;
Console. Write ("the area of circle with a diameter of {0} is {1}" diameter, diameter * Math. Pi );
}
We have definedThreadstart type DelegationThis delegate develops the method to be executed by the thread: Calculate, in which the area of a circle with a diameter of 0.5 is calculated and output. this constitutes the simplest example of multithreading. In many cases, this is enough, and the threadstart delegate is defined as void threadstart (), that is, the method to be executed does not have parameters. This is obviously a huge deficiency. To make up for this defect, a smart programmer has come up with many good methods, we will introduce it in the section that needs to pass multiple parameters. Here we will first introduce it. net another delegate set to solve this problem: parameterizedthreadstart, which will be detailed below.
A single parameter needs to be passed
Parameterthreadstart is defined as void parameterizedthreadstart (object state )?? The startup function of the thread defined by this delegate can accept an input parameter. The example is as follows:
Parameterizedthreadstart threadstart = new parameterizedthreadstart (calculate)
Thread thread = new thread ()
Thread. Start (0.9 );
Public void calculate (Object Arg ){
Double diameter = double (ARG );
Console. Write ("the area of circle with a diameter of {0} is {1}" diameter, diameter * Math. Pi );
}
Example 2
The calculate method has a parameter of the object type. Although there is only one parameter and it is of the object type, type conversion is still required when using it, but fortunately there is a parameter, in addition, by combining multiple parameters into a class, and passing the instance of this class as a parameter, multiple parameters can be passed.
Multiple parameters need to be passed
Although multiple parameters can be transferred by entrusting parameterizedthreadstart by encapsulating the required parameters into a class, the parameter conversion is inevitable because the input parameter of this delegate is an object, there are several common parameter passing methods below. Let's take a look at them one by one.
Use special thread classes
This is a classic mode that many programmers love to use. Simply put, they put the methods that need to be executed by another thread and the parameters they need into a class, and the parameters are used as the attributes of the class, declare the instance during the call, and then initialize the attributes. When the method is executed, use the initialized attributes in the class for execution, so that the method itself does not need parameters, the multi-parameter transfer effect is achieved, so we can use the threadstart delegation without parameters mentioned at the beginning of this article, and because the methods and parameters to be executed are put in a class, fully embodies the characteristics of object-oriented. the procedure is as follows:
We use a class to package the method for calculating the area. The input parameter diameter (diameter) is a field of this class.
Public class mythread
{
Public double diameter = 10;
Public double result = 0;
Public mythread (INT diameter)
{
This. Diameter = diameter;
}
Public void calculate ()
{
Console. writeline ("Calculate start ");
Thread. Sleep (2000 );
Result = diameter * Math. Pi ;;
Console. writeline ("calculate end, diameter is {0}, result is {1}", this. diameter, result );
}
}
Mythread T = new mythread (5.0 );
Threadstart = new threadstart (T. Calculate)
Thread thread = new thread (threadstart );
Thread. Start ();
Example 3
In this way, the parameter transfer is changed to attribute sharing. It is good to encapsulate the data involved in the logic and the logic in terms of encapsulation, this method also has a clever variant that uses the anonymous method. This variant can be used to save independent classes. Now I am giving this method.
Double diameter = 6;
Double result = 0;
Thread TA = new thread (New threadstart (delegate ()
{
Thread. Sleep (2000 );
Result = diameter * Math. Pi;
Console. writeline ("anonymous calculate end, diameter is {0}, result is {1}", diameter, result );;
}));
Ta. Start ();
Example 4
This method is the same as in the preceding example. It changes the parameter transfer to the call to the variable, thus canceling the parameter transfer. However, the latter makes full use of a property of the anonymous method, you can directly use the local variables of the current context, suchDelegateOf course, the disadvantage of doing so is that if the anonymous method is too long, the program's readability will be reduced, so few people usually do this. Here we provide this method for your reference.
Smart readers must think that, since fields can be used to input variables, you can also use fields to output variables, for example, in the above two examples, we can see that the calculation results are written into a variable named result (highlighted). Can we directly access this variable to get the calculation result?
In this case, there is a fatal problem: Since it is asynchronous execution, how does the main thread know when the split thread completes the calculation? For example, in the above two examples, our threads are sleeping for 2000 milliseconds before calculation. If the main thread accesses the result before the computation is completed, only one 0 value can be obtained. so we have a series of solutions below.
Parameters need to be passed and return parameters
As mentioned earlier, the main thread needs to know when the sub-thread is executed. You can use the thread. threadstate enumeration to determine when the sub-thread is executed.
When the threadstate of the thread is = threadstate. when you stop the job, it usually means that the thread has finished the job and the result is available. If it is not in this status, continue to execute other jobs, or wait for a while and try again. if we need to wait for multiple subthreads to return data and use their results for asynchronous computing, it is called thread synchronization, next, we will introduce another method that I recommend. It can customize the number of parameters and return data, which is relatively convenient to use.
Use delegate asynchronous call methods and callbacks
First, we need to define the method to be called asynchronously as a delegate, and then use begininvoke for asynchronous call. The first parameter of begininvoke is the diameter, the second is the method called after the thread is executed.
Delegate double calculatemethod (double diameter );
Static calculatemethod calcmethod;
Double result = 0;
Static void main (string [] ARGs)
{
Calcmethod = new calculatemethod (calculate );
Calcmethod. begininvoke (5, new asynccallback (taskfinished), null );
}
///
/// The function called by the thread
///
Public static double calculate (double diameter)
{
Return diameter * Math. Pi;
}
///
/// The callback function after the thread is completed
///
Public static void taskfinished (iasyncresult result)
{
Result = calcmethod. endinvoke (result );
}
Example 5
Note: In the taskfinished method executed after the thread execution is complete, we use endinvoke to obtain the return value of this function.
Thread Pool
Although a thread is a good thing, it is also a large resource consumer. In many cases, we need to use multiple threads, but we do not want too many threads. This is the role of the thread pool ,. NET provides a ready-made thread pool threadpool. Its usage is as follows:
Waitcallback W = new waitcallback (calculate );
Threadpool. queueuserworkitem (W, 1.0 );
Threadpool. queueuserworkitem (W, 2.0 );
Threadpool. queueuserworkitem (W, 3.0 );
Threadpool. queueuserworkitem (W, 4.0 );
Public static void calculate (double diameter)
{
Return diameter * Math. Pi;
}
Example 6
First define a waitcallback delegate. The waitcallback format is void waitcallback (Object State). That is to say, your method must conform to this format. Then call queueuserworkitem to add the task to the thread pool, when the county city pool has a idle line, your code will be scheduled and run.
Every process has a thread pool. The default thread pool size is 25. We can set its maximum value through the setmaxthreads method.
[Note]Since each process has only one thread pool, if the thread pool is used in IIS or sqlserver processes and the maximum capacity of the thread pool needs to be set, it will affect the IIS process or SQL process, so be careful in both cases.
Control
When talking with everyone, I found that all colleagues who are used to object-oriented thinking are always troubled by the execution context in the multi-thread scenario. For example, in Example 5, the main program starts the subthread to execute the calculate method, and calls back taskfinished after execution. If the main thread ID is 1, The subthread ID is 2, so calculate must be executed in the thread id = 2. What about the taskfinished callback function? It is also executed in the context of the thread whose ID is 2. If you do not believe it, try to output the thread ID. This is usually not a problem, but when we need to use the subthread in winform programming, this may cause problems. We will discuss this issue below.
Special features of multi-thread programming for form programs
When we move the callback code in Example 5 to winform, we can see the problem.
Public static void taskfinished (iasyncresult result)
{
Result = calcmethod. endinvoke (result );
This. textbox1.text = result;
}
The purpose of the program is to write a textbox into the result after the thread completes execution. However, when the program runs this. textbox1.text = result. in the past, winform had strict requirements on threads. Apart from the threads used to create these controls, other threads do not allow cross-thread access to the properties and methods of controls on winform (except for a few special attributes). In some versions of the system, such as XP, this issue is handled, cross-thread control access can be executed, but most Windows systems are not. If we do need to modify the control attributes across threads or call the control method, you must use the control method invoke. This method can switch the execution context back to the thread for creating these controls. The specific operations are as follows:
Delegate void changetext (string result );
Public static void taskfinished (iasyncresult result)
{
Result = calcmethod. endinvoke (result );
This. begininvoke (New changetext (this. textbox1.appendtext), T. Result. tostring ())
}
Because methods must be used in the delegate, I use the appendtext method instead of directly setting the text attribute. If you want to set the text attribute, you must encapsulate a method and connect it to the delegate.