Applications that can perform multiple tasks at the same time can be written. This capability (called "multi-threaded processing" or "free thread processing") is a powerful method for designing a processor intensive and requires user input. Components of calculating wage information is an example of a component that may utilize multi-threaded processing. This component can process the user on a thread into the database input to the database, and perform frequency use of the salary table for frequent use of the processor on another thread. By running these processes on different threads, users do not have to wait until the computer completes the calculation, you can enter other data. In this exercise, a simple multi-threaded component will be created, which can perform several complex calculations at the same time. Creating a project application will include a single form and a component. The user will enter the value and indicate that the component begins to calculate. Then, the form will receive a value from the component and display it in the tag control. The component will perform frequently using the processor's calculation and notify the form after completion. You will create a common variable in the component to save the value received from the user interface. At the same time, you will also implement some methods in the component, and perform calculations based on the values of these variables.
Note that although the functionality of the calculated value is usually more desirable, the parameters cannot be transferred between the threads, and the value cannot be returned. There are many simple ways to provide values and from thread reception values. In this presentation, the value will be returned to the user interface by updating the common variable. When the thread is executed, the transaction is used to notify the main program.
Create a form
Create a new "Windows Application" project. Name the application Calculation and rename the Form1.cs as frMcalculation.cs. This form will use as the primary user interface of the application. Double-click the form on the designer to open the code editor. In the Edit menu, select Find and Replace, then select Replace. Use "All Replace" to replace Form1 to frmcalculation. In the Solution Explorer, right-click "frMcalculation.cs" and select View Designer. The designer opens. Add 5 Label controls, 4 Button controls, and 1 TextBox control to the form. Setting properties for these controls as follows:
Control Name text Label1lblFactorial1 (blank) Label2lblFactorial2 (blank) Label3lblAddTwo (blank) Label4lblRunLoops (blank) Label5lblTotalCalculations (blank) Button1btnFactorial1FactorialButton2btnFactorial2Factorial - 1Button3btnAddTwoAdd TwoButton4btnRunLoopsRun a LoopTextbox1txtValue (blank)
Create a Calculator component
Select "Add Components" from the Project menu. Name the component Calculator. Add public variables to Calculator components
Open the code editor for Calculator. Add a statement that creates a common variable, which is used to pass the value from frmcalculation to each thread. Variable VartoTotalcalculations will retain the cumulative value of the total number of calculations executed by the component, while other variables will receive values from the form. Public Int Varaddtwo;
Public int varFact1;
Public int varFact2;
Public int varoloopvalue;
Public double varotalcalculation = 0; add methods and events to Calculator components
For event declaration, the component will use these events to transfer values to the form.
Note that although you will declare 4 events, because the two events will have the same signature, only 3 delegates are required. Turn them under the previously entered variable declaration, type the following code: // this delegate will be invoked with tour of your service.
Public Delegate Void FactorialCompleteHandler (Double Factorial, Double Totalcalcalculation);
Public Delegate Void AddTwocompleteHandler (int Result, Double Totalcalculation)
Public Delegate Void LoopCompleteHandler (int counter); declaration components will be used to communicate with the application. To achieve this, the following code is added to the following code. Public Event FactorialCompleteHandler FactorialComplete;
Public Event FactorialCompleteHandler FactorialminusoneComplete;
Public Event AddTwocPletehandler AddTwocPlete;
Public Event LoopCompleteHandler loopComplete; below the code that is typed, type the following code: // this method Will Calculate The Value of a Number Minus 1 Factorial
// (Varfact2-1!).
Public void factorialminusone ()
{
Double VartotalasofNow = 0;
Double varresult = 1;
// Performs a factorial calculation on varFact2 - 1.
For (int varx = 1; varX <= varFact2 - 1; varX )
{
VarResult * = varX;
// increments Vartotalcalcalculation and keeps track of the current
// Total as of this instant.
Vartotalcalculations = 1;
Vartotalasofnow = varotalcalculation;
}
// Signals That The Method Has Completed, And Communicates T
// Result and a value of total calculation performed up to this
// Point.
FactorialminusoneComplete (varresult, varotalasofnow);
}
// this Method Will Calculate The Value of a Number Factorial.
// (Varfact1!)
Public void factorial ()
{
Double varresult = 1;
Double VartotalasofNow = 0;
For (int varx = 1; varX <= varFact1; varX )
{
VarResult * = varX;
Vartotalcalculations = 1; VartotalasofNow = Vartotalcalcalculations;
}
FactorialComplete (VarResult, VartoTalasofNow);
}
// this Method Will Add Two to a Number (VaraddTwo 2).
Public void addtwo ()
{
Double VartotalasofNow = 0;
Int varResult = varAddTWO 2;
Vartotalcalculations = 1;
Vartotalasofnow = varotalcalculation;
AddTWocomplete (VarResult, Vartotalasofnow);
}
// this Method Will Run a loop with a nested loop varloopvalue Times.
Public void runaloP ()
{
Int varX;
Double VartotalasofNow = 0;
For (varX = 1; varX <= varLoopValue; varX )
{
// this nested loop is added solely for the purpose of Slowing Down
// The Program and Creating A Processor-Intensive Application.
For (int vary = 1; vary <= 500; var)
{
Vartotalcalculations = 1;
Vartotalasofnow = varotalcalculation;
}
}
LoopComplete (VARTOTALASOFNOW, VARLOOPVALUE);
} Transfer user input to the component The next step is to add code to frmcalculations to receive user input, and receive valtor components and transmit values to it. Realize front end function of frMcalculations
Open frMcalculations in the code editor. Find the Public Class FRMCALCULATIONS statement. Type the {below: Calculator Calculator1; find the constructor. Before}, add: // Creates a new instance of calculator.
Calculator1 = New Calculator (); Click Each button in the Designer to generate a code outline for each control, and add code to create these handles. After completing, click the event handler to look similar to the following form: Private Void BtnFactorial1_Click (Object Sender, System.EventArgs E)
// passes the value type type. .. Varfact1.
{
Calculator1.varfact1 = int.parse (TXTVALUE.TEXT);
// disables the btnfactorial1 Until this Calculation is Complete.
btnfactorial1.enabled = false;
Calculator1.factorial ();
}
Private void btnfactorial2_click (object sender, system.eventargs e) {
Calculator1.varfact2 = int.parse (txtValue.text);
btnfactorial2.enabled = false;
Calculator1.factorialminusone ();
}
Private void btnaddtwo_click (Object Sender, System.Eventargs E)
{
Calculator1.varaddtwo = int.parse (txtvalue.text);
btnaddtwo.enabled = false;
Calculator1.addtwo ();
}
Private void btnrunloops_click (Object Sender, System.Eventargs E)
{
Calculator1.varloopValue = int.parse (txtvalue.text);
Btnrunloops.enabled = false;
// lets the user know That a loop is running
LBLRUNLOOPS.TEXT = "looping";
Calculator1.runaloop ();
} Under the previous step, type the following code to process the form from Calculator1: Protected Value, Double Calculations
// Displays the returned value in the appropriate label.
{
Lblfactorial1.text = value.toString ();
// Re-enables The Button SO it can be used again.
BTNFACTORIAL1.ENABLED = TRUE;
// Updates the lael this displays the Total Calculation Performed
Lbltotalcalculations.text = "Totalcalculations Are"
Calculations.toString ();
}
Protected Void Factorialminushandler (Double Value, Double Calculation)
{
Lblfactorial2.text = value.toString ();
BTNFACTORIAL2.ENABLED = True;
Lbltotalcalculations.text = "Totalcalculations Are"
Calculations.toString ();
}
Protected Void AddTwoHandler (int value, double cagculation)
{
LBLADDTWO.TEXT = VALUE.TOSTRING ();
Btnaddtwo.enabled = true;
Lbltotalcalculations.text = "Totalcalculations Are"
Calculations.toString ();
}
Protected Void LoopdoneHandler (Double Calculation, Int Count)
{
Btnrunloops.enabled = true;
LBLRUNLOOPS.TEXT = count.toString (); lbltotalcalculations.text = "totalcalculations are"
Calculations.toString ();
} In the constructor of FRMCALCULATIONS, the following code is added next to} to handle custom events that will receive from Calculator1: Calculator1.FactorialComplete = New
Calculator.FactorialCompleteHandler (this.FactorialHandler);
Calculator1.factorialminusoneComplete = New
Calculator.FactorialCompleteHandler (this.Factorialminushandler);
Calculator1.addtwocplete = new
Calculator.addwockPletehandler (this.addtwohandler);
Calculator1.loopComplete = New
Calculator.loopCompleteHandler; Test Application Now the project has been created, which will be able to perform several complex computing components combined with the form. Although multi-threaded processing is not implemented, the project should be tested before proceeding to verify its function. Test items
Select "Start" from the Debug menu. The application starts and displays frMcalculations. Type 4 in the text box, then click the button labeled "Add Two". Digital "6" should be displayed in the tab below the button, and "Total Calcal CalcalcalCalculation should be displayed" Total Calculations Are 1 ". Click the button marked as "Section - 1". The number "6" should be displayed below the button, and "Total CalcalCalcalCalculation" should now be displayed "Total Calculations Are 4". Change the value in the text box to 20, then click the button labeled "step". The following displays the number "2.43290200817664e 18" below, and LBLTOTAlCalcalCalculations are now displayed as "Total Calculation Are 24". Change the value in the text box to 50000, and then click the button labeled "Run Circulation". Note that there is a short-lived interval before this button is re-enabled. The label under this button should display "50000", and the total number of calculations is displayed as "25000024". Change the value in the text box to 5000000 and click the button marked as "Run loop", follow the button that is marked as "Add Two". Click it again. Until the loop has been completed, the button and any of the controls on the form respond. If the program only runs a single execution thread, the calculation of frequent use of the processor similar to the above example tends to occupy the program until the calculation has been completed. In the next section, you will add a multi-threaded process to your application so that multiple threads can be run at a time.
Adding Multi-threaded processing functions The above example demonstrates the limitations of only a single execution thread. In the next section, you will use
Thread class
The object adds multiple execution threads to the component. Add threads subroutine
Open Calculator.cs in the code editor. In the vicinity of the top of the code, find the class declaration, follow the following code, type the following code: // Declares the variables you will use to hold your thread Objects.public system.threading.thread factorythread;
Public system.threading.thread factorialminusonethread;
Public system.threading.thread addtwothread;
Public system.threading.thread loopthread; Before the end of the class declaration, add the following method: Public Void ChooseThreads (int threadnumber)
{
// determines Which thread to Start Based on The Value IT Receives.
Switch (threadnumber)
{
Case 1:
// sets the thread using the addressof the subroutine where
// The thread will start.
Factorialthread = new system.threading.thread (New
System.threading.ThreadStart (this.factorial);
// Starts the thread.
Factorialthread.start ();
Break;
Case 2:
FactorialminusonThread = New
System.threading.thread (New
System.threading.ThreadStart (this.Factorialminusone);
FactorialminusonethRead.start ();
Break;
Case 3:
Addtwothread = new system.threading.thread (New
System.threading.ThreadStart (this.addtwo);
AddtwothRead.start ();
Break;
Case 4:
Loopthread = new system.threading.thread (New
System.threading.ThreadStart (this.Runaloop);
Loopthread.start ();
Break;
}
} When the Thread object is instantiated, it requires a parameter in the form of a ThreadStart object. The ThreadStart object is a delegation of the address of the method of starting thread. The ThreadStart object cannot accept parameters or transfer values, so it can only represent a Void method. The ChooseThreads method just implemented will receive a value from the program that calls it and uses this value to determine the appropriate thread to be started. Add appropriate code to frmcalculations
Open the frMcalculation.cs file in the code editor, then find protected void btnfactorial1_click.
Note Down to call the Calculator1.Factorial1 method, as shown below: // Calculator1.Factorial () Add the following list to call Calculator1.choosethreads: // Passs the value 1 to Calculator1, thus Directing it to start the
// Correct thread.calculator1.choosethreads (1); similar modifications to other Button_Click subroutines.
Be careful
The Threads parameter contains the appropriate value.
After completing, the code should look like the following form: protected void btnfactorial1_click (Object Sender, System.EventArgs E)
// passes the value type type in the txtvalue to calculator.varfact1
{
Calculator1.varfact1 = int.parse (TXTVALUE.TEXT);
// disables the btnfactorial1 Until this Calculation IS Complete
btnfactorial1.enabled = false;
// Calculator1.Factorial ();
Calculator1.choosethreads (1);
}
Protected void btnfactorial2_click (Object Sender, System.Eventargs E)
{
Calculator1.varfact2 = int.parse (txtValue.text);
btnfactorial2.enabled = false;
// Calculator1.Factorialminusone ();
Calculator1.choosethreads (2);
}
Protected void btnaddtwo_click (Object Sender, System.Eventargs E)
{
Calculator1.varaddtwo = int.parse (txtvalue.text);
btnaddtwo.enabled = false;
// Calculator1.addtwo ();
Calculator1.choosethreads (3);
}
Protected void btnrunloops_click (Object Sender, System.Eventargs E)
{
Calculator1.varloopValue = int.parse (txtvalue.text);
Btnrunloops.enabled = false;
// lets the user know That a loop is running
LBLRUNLOOPS.TEXT = "looping";
// Calculator1.Runaloop ();
Calculator1.choosethreads (4);
} Sending processing The call to the control will now increase the display update on the form. Given that the control is always made by the main implementation thread, any call to the control is required to call any call in the slave thread. Seale processing is a behavior that is called across thread boundary and needs to spend a lot of resources. In order to minimize the amount of sealed processing that needs to occur, it should be used to handle the call safely.
Control.BeginInvoke method
To call the master's method on the thread, thereby minimizing the encapsulated processing of the threaded boundary that must occur. This call is necessary when the method of the operation control is called. For more information, see Operating Controls from the thread. Create a control call process
Open the code editor for FRMCALCULATIONS. In the declaration section, add the following code: Public Delegate Void Fhandler (Double Value, Double Calculation);
Public Delegate Void A2Handler (int value, double cagculation);
Public Delegate Void Ldhandler (Double Calculation, Int Count); Invoke and BeginInvoke requires delegation to the appropriate method as a parameter. These code line declare some entrustment signatures, which will be used by BeginInvoke to call the appropriate method. Add a lowering method to the code. Public Void FactHandler (Double Value, Double Calculation) {
}
Public void Fact1Handler (Double Value, Double Calculation)
{
}
Public Void Add2Handler (int value, double cagculation)
{
}
Public void ldoneHandler (Double Calculation, Int Count)
{
} In the Edit menu, use "Cut" and "Paste", cut all the code from the FactorialHandler method and paste it into the FactHandler. Repeat the above steps for Factorialminushandler and Fact1Handler, AddTwoHandler, and Add2handler and LoopdoneHandler and LDONEHANDLER. After completing, there should be no residual code in FactorialHandler, Factorial1Handler, AddTwoHandler, and LoopDoneHandler, and all code they have included should have moved to the appropriate new method. Call the BeginInvoke method to invoke these methods asynchronously. BeGinInvoke can be called from any control on the form (this) or in the form. After completing, the code should look like the following form: Protected Value, Double Calculation
{
// beginInvoke causes asynchronous execution to begin at the address
// specified by the delegate. Simply Put, IT Transfers Execution of
// this Method Back to the main thread. Any parameters required by
// the method contained at the delegate area wrapped in an Object and
// passed.
this.beginInvoke (New Fhandler (FactHandler), New Object []
{Value, Calculations};
}
Protected Void Factorialminushandler (Double Value, Double Calculation)
{
This.BeginInvoke (New Fhandler (Fact1Handler), New Object []
{Value, Calculations};
}
Protected Void AddTwoHandler (int value, double cagculation)
{
This.BeginInvoke (New A2Handler (Add2handler), New Object []
{Value, Calculations};
}
Protected Void LoopdoneHandler (Double Calculation, Int Count)
{
This.BeginInvoke (New Ldhandler (LDONEHANDAL), New Object []
{CALCULATIONS, Count};} It seems that the event handler is only called the next method. In fact, the event handler implements a call method on the main operation thread. This approach saves the call to the thread boundary and enables multi-threaded applications to run effectively without having to worry about deadlocks. For more information on using controls in multithreaded environments, see Operating Controls from the thread. Save your work. Select "Start" from the Debug menu to test the solution.
Type 10000000 in the text box and click Run Loop. "Looping" is displayed in the label below this button. It should take a long time to run this cycle. If it is done too fast, adjust the size of the number accordingly. Quickly click the three buttons that are still enabled. You will find that all buttons respond to your input. The label below "Add TWO" should be the first display result. The results will be displayed in the label below the step button. It is estimated that these results will be infinite, because the numbers returned by the 10,000,000 steps are too large for the double precision variable, so that it is outside it. Finally, after a while, the result will be returned to the "Running Circulation" button. As just observed, four sets of independent calculations were performed simultaneously on four separate threads. The user interface keeps the response to the input and returns the result after each thread is completed. Coordination Threads Experience Multi-threaded application users may find subtle defects in typed code. Return the following code line from each execution calculated subroutine from Calculator.cs: Vartotalcalculation = 1;
Vartotalasofnow = varotalcalculations; these two lines of code increments the public variable Vartotalcalculations and set the local variable VartoTalasOfNow to this value. This value is then returned to FRMCALCULATIONS and is displayed in the tag control. But is the value returned correctly? If only a single execution thread is running, the answer is obviously correct. But if there are multiple threads running, the answer becomes less sure. Each thread has the ability to increment VARTOTALCALCALCULATIONS. This is likely to occur: After a thread is incremented by the variable, the other thread may change its value by incrementing the variable before it copies this value to VartoTalasofnow. This will result in possible each thread that is actually incorrect reporting. Visual C # provides
LOCK statement
The statement is allowed to allow threads to ensure that each thread always returns an accurate result. Lock's grammar is as follows: Lock (AnObject)
{
// INSERT Code That Affects the Object.
// INSERT More Code That Affects the Object.
// INSERT More Code That Affects the Object.
// Release the Lock.
} After entering the LOCK block, the execution of the specified expression has been blocked before the specified object has a dedicated lock. In the example shown above, the execution of AnObject is in a locked state. You must use the LOCK to return the objects (rather than returned values). Then, the execution is continued in the form of a block without being interfered with other threads. The statement set as a unit is called "atom". When you encounter}, the expression will be released and threads can continue to work. Add a LOCK statement to an application
Open Calculator.cs in the code editor. Find every instance of the following code: VartotalcalcalCulation = 1;
Vartotalasofnow = varotalcalculations; there should be four instances of this code, and one in each calculation method. Modify this code to display as follows: Lock (this) {
Vartotalcalculations = 1;
Vartotalasofnow = varotalcalculation;
} Save the work and test it as shown in the example. You may notice a subtle impact on program performance. This is because the execution of the thread is stopped when the component gets his lock. Although it guarantees the correctness, this approach offsets certain performance advantages of multithreading. The necessity of locking threads should be carefully considered, and it is only true when absolutely necessary.