Using BackgroundWorker to implement multithreading in C #

Source: Internet
Author: User
Tags visual studio 2010

Original address: Multithreading Using a Background Worker, C #

Introduced

When developing a Windows Forms application, you will often notice that when a time-consuming operation is performed, such as processing a file or requesting data from a remote server, the user interface goes into suspended animation. This is because your application is running under a single thread. This thread is responsible for responding to the user interface and is also responsible for handling all events and methods in the application. Therefore, a time-consuming operation will block your user interface until the operation is complete. Today, what we're going to do is move these time-consuming operations to another different thread, so that you can keep the user interface smooth as you do it on the other side.

Background

In this example, we will use the Microsoft BackgroundWorker class, and more information about this class can be found here.

We will create a simple application that performs time-consuming operations and displays the final result as a user. The time-consuming operation will be executed in another thread, and the user interface prompts will continue to be updated as the operation progresses during the execution of the operation. We will allow the user to cancel the operation at any time.

Keep in mind that only the main thread can access the user interface, in other words, you cannot access the user control in another thread. Below we will explain in detail.

Using code

I'll show you the code that's used in the application, and finally I'll attach the source code.

Create an application

We're going to create a simple Windows Forms application in Microsoft VS, I'm using Visual Studio 2010. Create a new Windows Forms application like the same. I prefer to use C #, and you can also use vb.net.

Set the layout as the same. I personally like to organize my controls with the table layout panel. This keeps the space in order, when the form is enlarged or resized. What we're going to do is add a textbox (set to Multiline mode) to show the results of the worker threads, a numbericupanddown allows us to dazzle the numbers, a Start button and an end button.

In the Toolbox, under Menus and toolbars, select Add a StatusStrip. This allows us to add a status tag where we will show the progress information to the user on the label.

In StatusStrip, click the small arrow in the lower left corner and select Add a Statuslabel. Rename the label to Lblstatus, and the package its Text property is set to null.

In the form's code, we declare an object of type BackgroundWorker:

Private New BackgroundWorker ();

In the form's constructor, initialize the worker we just created with the following properties:

    • The DoWork event handler is called when the background worker begins to work asynchronously. It is in this event that we deal with time-consuming operations, such as invoking a remote server, querying a database, processing files ... This event is executed on a new thread, which means that we cannot access the user control in this method.
    • The runwokercompleted event is called when the background worker completes the operation, cancels the operation, or an exception occurs. This event is called on the main thread, which means that we can access the user control in this method.
    • The ProgressChanged event handler is triggered when the reportprogress of the background worker is called. We use this method to write progress information to the user interface. This event is called on the main thread, meaning that we have access to the user control in this method.
    • The Workerreportsprogress property, which indicates whether the worker can report progress to the main thread.
    • The Workersupportscancellation property, which indicates whether the worker can cancel the operation based on the user's request.

The following is the completion code of the constructor:

 Public Form1 ()        {            InitializeComponent ();             New Doworkeventhandler (myworker_dowork);             New Runworkercompletedeventhandler (myworker_runworkercompleted);             New Progresschangedeventhandler (myworker_progresschanged);             true ;             true ;        }

Now let's declare the event handler function for the worker:

    • The DoWork event handler function has two parameters, one sender, and one DoWorkEventArgs parameter
protected void myworker_dowork (object  sender, DoWorkEventArgs e) {}
    • The RunWorkerCompleted event handler function has two parameters, one sender, and one Runworkercompletedeventargs parameter
protected void myworker_runworkercompleted (object  sender, Runworkercompletedeventargs e) {}
    • The ProgressChanged event handler function has two parameters, one sender, and one ProgressChangedEventArgs parameter
protected void myworker_progresschanged (object  sender, ProgressChangedEventArgs e) {}

Here we create a helper method that receives an integer multiplied by 1000, the thread sleep250ms, and then returns the result. This is a simulation of the time-consuming operations your application may perform, and you can change the interval of sleep. Note that this method should be called in the DoWork event, which is that the background thread will sleep the given time, not the main thread. The functions are as follows:

Private int Performheavyoperation (int  i) {    System.Threading.Thread.Sleep (+);     return  + ;}

Go to the design interface and double-click the Start button to go to its event handler. What we're going to do is get the value from the Numericupanddown control, pass that value to the async thread, and start executing the background worker. We need to get the value of the numeric control here, because once we get to the new thread, we can't access the user control Wolf. To begin the execution of the background worker, we called the RunWorkerAsync method. This method receives an object parameter, which is passed to the background thread, in which we can put the values of any number of controls. In order to pass in more than one value, we use an array of object. The following is the completion code for btnStart_Click. Note that the worker being executed cannot be called again, and you will get a run-time error if you do so.

Private voidbtnStart_Click (Objectsender, EventArgs e) {    intNumericvalue = (int) Numericupdownmax.value;//Capture the user input    Object[] Arrobjects =New Object[] {numericvalue};//Declare The array of objects    if(!myworker.isbusy)//Check If the worker is already in progress{btnstart.enabled=false;//Disable the Start buttonMyworker.runworkerasync (arrobjects);//Call the background worker    }}

Now in the DoWork event handler, we can handle all the time-consuming operations. First, we receive the objects obtained from the main thread, then process them, and finally return the results to the main thread. Remember that only the main thread can access the user control. When we deal with values, we always do two things:

    • Report progress to the main thread using the ReportProgress method
    • Check the Cancellationpeding property of the background worker to see if the user has a cancel command

Finally, we put the results in the result property of the DoWorkEventArgs parameter, which will be captured by the RunWorkerCompleted event. The following is the complete code for DoWork:

protected voidMyworker_dowork (Objectsender, DoWorkEventArgs e) {BackgroundWorker Sendingworker=(BackgroundWorker) sender;//Capture The BackgroundWorker that fired the event    Object[] Arrobjects =     (Object[]) e.argument;//Collect The array of objects the we received from the main thread    intMaxValue = (int) arrobjects[0];//Get the numeric value//From inside the objects array, and don ' t forget to castStringBuilder SB =NewStringBuilder ();//Declare A new string builder to store the result.     for(inti =1; I <= MaxValue; i++)//Start A For loop    {        if(!sendingworker.cancellationpending)//at each iteration of the loop,//Check if there is a cancellation request pending{sb. Append (string. Format ("counting number: {0}{1}", Performheavyoperation (i), Environment.NewLine));//Append The result to the String builderSendingworker.reportprogress (i);//Report we progress to the main thread        }        Else{E.cancel=true;//If A cancellation request is pending, assign this flag a value of true             Break;//If A cancellation request is pending, break to exit the loop}} E.result= sb. ToString ();//Send Our result to the main thread!}

We then deal with the ProgressChanged event. We get the integer value passed in when the ReportProgress method is called. Note that you can pass any type of object by using the Userstate property of the ProgressChangedEventArgs parameter. In addition to displaying the status information, you can also use the progress bar to display the current progress.

protected void myworker_progresschanged (object  sender, ProgressChangedEventArgs e) {    //  Show The progress to the user based on the input we got from the background worker    string. Format ("counting number: {0} ... " , e.progresspercentage);}

followed by the RunWorkerCompleted event. We first check to see if the worker is canceled, or if any errors have occurred. We then obtained the results of the background worker calculation, which we used to display to the user:

protected voidMyworker_runworkercompleted (Objectsender, Runworkercompletedeventargs e) {    if(!e.cancelled &&E.error==NULL)//Check If the worker has been canceled or if an error occurred    {        stringresult = (string) E.result;//Get The result from the background threadTxtresult.text = result;//Display The result to the userLblstatus.text =" Done"; }    Else if(e.cancelled) {Lblstatus.text="User Canceled"; }    Else{Lblstatus.text="An error has occurred"; } btnstart.enabled=true;//Re Enable the Start button}

The last thing left is that we need to implement the Cancel button. Double-click the Cancel button to enter the background code and invoke the CancelAsync method of the background worker. This will set the worker's cancellationpending flag to true. We will check this flag in the loop of the DoWork event handler. At this point, we can conclude that terminating a running BackgroundWorker does not take effect immediately, and if the background worker is dealing with something, we need to wait for it to complete before canceling the operation. Here is the code for Btncancel_click:

Private void Btncancel_click (object  sender, EventArgs e) {    myworker.cancelasync (); // Issue A cancellation request to stop the background worker}

Finally, run the program as follows:

The operation ends as follows:

Using BackgroundWorker to implement multithreading in C #

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.