Backgroundworker class favorites
Some time ago, I used the backgroundworker class to make a simple program to read background data to the interface datagridview. Today I used this class again.
, Encountered some problems, here is a summary:
Overview:
The backgroundworker class allows you to run operations on individual dedicated threads. Time-consuming operations (such as downloads and database transactions) May
As a result, the user interface (UI) appears to be in the stopped response state. If you need a user interface that can respond, and face long delays related to such operations
Later, you can use the backgroundworker class to easily solve the problem.
Note the following in msdn:
Make sure that you do not operate any user interface objects in the dowork event handler. Instead, you should use progresschanged and
The runworkercompleted event communicates with the user interface.
It means that an error occurs when you directly access the interface control in the do_work event handler, because the do_work event handler is in different threads on the UI interface.
. To access the interface control (for example, add a grid to the toolstripprogressbar), you must call the invoke method of the control (This method requires
Delegate the processing method .) You can.
When this class is called, there are several parameter passing procedures:
1: When an asynchronous operation is started (the runworkerasync method is called to start), the system will pass the parameters (Object Type) of this method to the dowork event.
Handler (already on another thread)
This method will send a request to start asynchronous execution, which will trigger the dowork event and then start to execute background operations. If the background operations are already being executed
An invalidoperationexception is thrown. Whether the background operation is being executed can be determined by the backgroundworker. isbusy attribute.
Parameter Setting: runworkerasync (**********)
Parameter receiving: E. argument in the dowork event handler
2: When you call the reportprogress method of the backgroundworker instance, this method will trigger the progresschanged event, the parameter will be from reportprogress
When the method is uploaded to the progresschanged event handler, The backgroundworker instance must be able to report progress updates.
Parameter Setting: In the dowork event handler: reportprogress (**********,*********)
Parameter receiving: In the progresschanged event handler: E. progresspercentage and E. userstate
3: When the dowork event handler returns, the runworkercompleted event is triggered, and the parameters are transmitted from the dowork event handler.
To the runworkercompleted method, you can obtain the passed information from parameter E of this event.
Parameter Setting: In the dowork event handler, E. Result = ************;
Parameter receiving location: In the runasync event handler, E. Result = ************;
The above three points are easy to understand, but pay attention to them in actual operations.
When you use the control. Invoke method to access the interface control in the dowork event handler, You need to input the parameter of the method associated with the delegate. this parameter is
It is passed in as an object array. Even if the parameter of the delegate binding method itself is an object array, it cannot be passed directly, or another array needs to be constructed.
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/superfafafa/archive/2009/10/28/4737192.aspx
Asynchronous Operation Programming Using the backgroundworker component
Released on: 2008-06-18 | updated on: 2008-06-18
Author: Zheng Zuo
Abstract: This article introduces the functions of the backgroundworker component and its application in event-based Asynchronous Operation Programming, and briefly introduces the implementation principle of the component.
Download the sample code of backgroundworkersample related to this article.
Content on this page
Overview
Component Introduction
Application Example
Implementation Principle
Conclusion
Overview
In an application, you may encounter some time-consuming functional operations, such as data downloading, complex computing, and database transactions. Generally, such functions are implemented on separate threads, after the execution is completed, the result is displayed on the user interface. This prevents the user interface from responding for a long time. In. NET 2.0 and later versions, FCL provides the backgroundworker component to conveniently implement these functional requirements.
Component Introduction
The backgroundworker class is located in the namespace of system. componentmodel. It executes operations on individual threads to implement event-based asynchronous mode. The following describes the main members of the backgroundworker class.
The 1st main method of the backgroundworker class is runworkerasync, which submits a request to start the operation asynchronously. After a request is sent, a dowork event is triggered, run the asynchronous operation code in the event handler. The runworkerasync method signature is as follows,
Publicvoidrunworkerasync ();
Publicvoidrunworkerasync (Object argument );
If an asynchronous operation requires an operation parameter, it can be provided as an argument parameter. Because the parameter type is object, type conversion may be required during access.
The cancelasync method submits a request to terminate the asynchronous operation and sets the cancellationpending attribute to true. It should be noted that whether the cancelasync method is successfully called is related to the workersuppscanscancellation attribute. If asynchronous operations can be canceled, set the workersuppscanscancellation attribute to true. Otherwise, an exception is thrown when you call this method. The cancelasync method does not contain parameters. The method signature is as follows,
Publicvoid cancelasync ();
When the cancelasync method is called, the value of the cancellationpending attribute of backgroundworker is set to true. Therefore, when writing a helper method executed in a separate thread, the Code should regularly check the cancellationpending attribute, check whether this attribute has been set to true. If it is true, the execution of the auxiliary method should be ended. One thing to note is that the Code in the dowork event handler may have completed processing when the cancellation request is sent. Therefore, dowork event handlers or helper methods may miss the time to set the cancellationpending attribute to true. In this case, even if the cancelasync method is called to cancel the Asynchronous Operation Request, the cancelled flag of the runworkercompletedeventargs parameter in the runworkercompleted event handler will not be set to true, this is a race condition problem that often occurs in multi-threaded programming. Therefore, you need to consider it when writing code.
To track the progress of an asynchronous operation, the backgroundworker class provides the reportprogress method. Calling this method triggers the progresschanged event, register the event to obtain the asynchronous execution progress information in the event handler. The method signature is as follows:
Publicvoidreportprogress (INT percentprogress );
Publicvoidreportprogress (INT percentprogress, object userstate );
This method contains two versions. percentprogress indicates the progress percentage. The value ranges from 0 to 100, and userstate indicates the Custom User status.
Like the cancelasync method, when the workerreportsprogress attribute of backgroundworker is set to true, the reportprogress method is called successfully. Otherwise, an invalidoperationexception occurs.
The three attributes of backgroundworker are mentioned above. cancellationpending is used to indicate whether the operation has been canceled. workerreportsprogress and workersuppscanscancellation are used to set whether to allow progress reporting and cancel the operation respectively.
Publicboolcancellationpending {Get ;}
Publicboolworkerreportsprogress {Get; set ;}
Publicboolworkersuppscanscancellation {Get; set ;}
Another attribute used is isbusy,
Publicbool isbusy {Get ;}
This attribute is used to query whether the backgroundworker instance is running an asynchronous operation. If the backgroundworker is running an asynchronous operation, the value is true. Otherwise, the value is false.
The backgroundworker class contains three events. In the event processing program, asynchronous operations can be performed to assist code writing and interaction with the user interface information.
Publiceventdoworkeventhandler dowork;
Publiceventprogresschangedeventhandler progresschanged;
Publiceventrunworkercompletedeventhandler runworkercompleted;
The dowork event handler is used to call an auxiliary method for actual processing. Because the event handler is executed on a thread different from the UI, therefore, make sure that no user interface objects are operated in the dowork event handler. If the auxiliary method requires parameter support, it can be passed in through the runworkerasync method. In the dowork event handler, this parameter is extracted through the doworkeventargs. Argument attribute. During the asynchronous operation, you can use the progresschanged event handler to obtain the asynchronous operation progress information and use the runworkercompleted event handler to obtain the asynchronous operation result information, you can securely communicate with the user interface in the event handlers of progresschanged and runworkercompleted.
Application Example
The following is a simple example to demonstrate the typical applications of the backgroundworker component. In this example, a value summation operation is implemented, and the operation itself runs very fast. to simulate the processing process for a perceptible period, the thread. Sleep method is called in the auxiliary method.
The example program is displayed in Windows Forms, showing the sum operation for values from 1 to. The interface is as follows,
Figure 1: Application Interface
The following describes the main implementation code. First, let's take a look at the initialization of the backgroundworker class. During the initialization process, three events are registered, allowing asynchronous auxiliary method calls, asynchronous operation progress notifications, and Operation cancellation.
Private system. componentmodel. backgroundworker backgroundworker1;
Private void initializebackgoundworker ()
{
This. backgroundworker1 = new system. componentmodel. backgroundworker ();
This. backgroundworker1.workerreportsprogress = true;
This. backgroundworker1.workersuppscanscancellation = true;
This. backgroundworker1.dowork + = new doworkeventhandler (backgroundworker+dowork );
This. backgroundworker1.progresschanged + = new progresschangedeventhandler (backgroundworker1_progresschanged );
This. backgroundworker1.runworkercompleted + = new runworkercompletedeventhandler (backgroundworkerappsrunworkercompleted );
}
The startasync button event handler starts asynchronous processing of Operation requests. The event handler is as follows,
Private void startasyncbutton_click (Object sender, eventargs E)
{
Resultlabel. Text = string. empty;
This. numericupdown1.enabled = false;
This. startasyncbutton. Enabled = false;
This. cancelasyncbutton. Enabled = true;
// Obtain the calculated value.
Int numbertocompute = (INT) numericupdown1.value;
// Start the asynchronous operation.
Backgroundworker1.runworkerasync (numbertocompute );
}
The startasyncbutton_click handler first sets the status of some interface controls, then calls the runworkerasync method of the backgroundworker instance to start asynchronous operations, and then triggers the dowork event.
Void backgroundworkerincludowork (Object sender, doworkeventargs E)
{
Backgroundworker worker = sender as backgroundworker;
E. Result = computeadd (INT) E. argument, worker, e );
}
In the dowork event handler, use doworkeventargs. the argument attribute obtains the passed parameters and passes them to the computeadd auxiliary method. It saves the processing result to doworkeventargs. in the result property, the runworkercompletedeventargs of the runworkercompleted event handler is last added. obtain the processing result in the result attribute. If an exception occurs in the dowork event handler, backgroundworker will capture the exception and pass it to the runworkercompleted event handler. In this event handler, the exception information is exposed as the error attribute of runworkercompletedeventargs.
Private long computeadd (int n, backgroundworker worker, doworkeventargs E)
{
Long result = 0;
For (INT I = 1; I <= N; I ++)
{
If (worker. cancellationpending)
{
E. Cancel = true;
Break;
}
Else
{
Result + = I;
Thread. Sleep (500 );
Int percentcomplete = (INT) (float) I/(float) N * 100 );
Worker. reportprogress (percentcomplete );
}
}
Return result;
}
In the Helper method, the Code regularly accesses the cancellationpending attribute of the backgroundworker instance. If the cancelasync method of the backgroundworker is called, The cancellationpending attribute value is set to true, and the Helper method ends execution. In addition, the progress report function is implemented in the auxiliary method. The worker. reportprogress method is called to trigger the progresschanged event, and then the progress display is updated through the progresschanged event handler.
Void backgroundworker1_progresschanged (Object sender, progresschangedeventargs E)
{
This. progressbar1.value = E. progresspercentage;
}
Finally, you can obtain the asynchronous processing result information in the runworkercompleted event processing program, and analyze whether the asynchronous operation ends normally, is canceled during processing, or is terminated due to an error or exception in execution. There is a standard order for access to the processing result information. First, judge whether asynchronous processing ends abnormally, then determine whether the cancellation operation is performed, and finally access the processing result.
Void backgroundworkerappsrunworkercompleted (Object sender, runworkercompletedeventargs E)
{
If (E. Error! = NULL)
{
MessageBox. Show (E. Error. Message );
}
Else if (E. cancelled)
{
Resultlabel. Text = "canceled ";
}
Else
{
Resultlabel. Text = E. Result. tostring ();
}
This. numericupdown1.enabled = true;
Startasyncbutton. Enabled = true;
Cancelasyncbutton. Enabled = false;
}
In the preceding example, all functions are completed in a single window. You can simply modify the functions to display the progress in the independent dialog box and cancel the operation.
Figure 2: Progress display dialog box
Create a new form named processform to display the asynchronous operation progress. Modify the default constructor of the processform class, pass in the reference of the backgroundworker instance, and register the progresschanged event to update the form progress bar, register the runworkercompleted event to notify processform to close.
Public processform (backgroundworker backgroundworker1)
{
Initializecomponent ();
This. backgroundworker1 = backgroundworker1;
This. backgroundworker1.progresschanged + = new progresschangedeventhandler (backgroundworker1_progresschanged );
This. backgroundworker1.runworkercompleted + = new runworkercompletedeventhandler (backgroundworkerappsrunworkercompleted );
}
Void backgroundworkerappsrunworkercompleted (Object sender, runworkercompletedeventargs E)
{
This. Close ();
}
Void backgroundworker1_progresschanged (Object sender, progresschangedeventargs E)
{
This. progressbar1.value = E. progresspercentage;
}
Private void cancelbutton#click (Object sender, eventargs E)
{
This. backgroundworker1.cancelasync ();
This. cancelbutton1.enabled = false;
This. Close ();
}
The progress window can be displayed in a mode window or a non-mode window. The implementation code of the two is not much different. The improved startasync button event handler is as follows.
Private void startasyncbutton_click (Object sender, eventargs E)
{
//...
Backgroundworker1.runworkerasync (numbertocompute );
Processform form = new processform (this. backgroundworker1 );
Form. showdialog (this); // Mode
// Form. Show (this); // non-Mode
}
Implementation Principle
Before analyzing the implementation principles of backgroundworker, you need to understand the two newly added classes in. NET Framework 2.0. Both the asyncoperationmanager class and asyncoperation class are located in system. in the componentmodel namespace, The asyncoperation class supports tracking the lifetime of asynchronous operations, including operation progress notifications and operation completion notifications, make sure that the client's event handler is called in the correct thread or context.
Publicvoidpost (sendorpostcallback D, object Arg );
Publicvoidpostoperationcompleted (sendorpostcallback D, object Arg );
Call the POST method in the asynchronous auxiliary code to report the progress and intermediate results to the user. If the asynchronous task is canceled or the asynchronous task is completed, call the postoperationcompleted method to end the tracking life cycle of asynchronous operations. After the postoperationcompleted method is called, The asyncoperation object becomes no longer available, and an exception occurs when you access the object again. Both methods contain the sendorpostcallback delegate parameter. The signature is as follows,
Publicdelegatevoidsendorpostcallback (Object State );
The sendorpostcallback delegate is used to indicate the callback method to be executed when the message is about to be scheduled to the synchronization context.
The asyncoperation class looks powerful, but some developers have reported that the. NET 2.0 version of this class has bugs. Whether Microsoft has updated the version 3.0 and later requires further research. The author tests in the console application. The sendorpostcallback delegate submitted by the post method of asyncoperation is not necessarily executed in the main thread of the console. threading. thread. currentthread. managedthreadid can be used to confirm this. It is strange that no running exception is found in the console program. This may be the reason why the console program is executed differently from the form program.
The asyncoperationmanager class provides a convenient method for creating asyncoperation objects. You can use the createoperation method to create multiple asyncoperation instances to track multiple asynchronous operations.
The backgroundworker component implements operations on individual threads through the dowork event, which is completed through asynchronous delegation. The workerthreadstartdelegate delegate is declared within the backgroundworker class and the threadstart member variable is defined, at the same time, initialize threadstart In the constructor.
Private delegate void workerthreadstartdelegate (Object argument );
Private readonly workerthreadstartdelegate threadstart;
Public backgroundworker ()
{
This. threadstart = new workerthreadstartdelegate (this. workerthreadstart );
//...
}
Backgroundworker starts Asynchronous Operation requests by calling the runworkerasync method, and calls the threadstart. begininvoke method in the method body to Implement Asynchronous calls.
Public void runworkerasync (Object argument)
{
If (this. isrunning)
{
Throw new invalidoperationexception (Sr. getstring ("backgroundworker_workeralreadyrunning "));
}
This. isrunning = true;
This. cancellationpending = false;
This. asyncoperation = asyncoperationmanager. createoperation (null );
This. threadstart. begininvoke (argument, null, null );
}
The workerthreadstart method specified in the threadstart delegate triggers the dowork event. The user registers the dowork event to execute asynchronous code operations, the following code shows why the dowork event handler cannot access the UI element.
Private void workerthreadstart (Object argument)
{
Object result = NULL;
Exception error = NULL;
Bool canceled = false;
Try
{
Doworkeventargs E = new doworkeventargs (argument );
This. ondowork (E );
If (E. Cancel)
{
Canceled = true;
}
Else
{
Result = E. result;
}
}
Catch (exception exception2)
{
Error = prediction2;
}
Runworkercompletedeventargs Arg = new runworkercompletedeventargs (result, error, canceled );
This. asyncoperation. postoperationcompleted (this. operationcompleted, ARG );
}
In the above Code, this. the ondowork (e) method generates a dowork event. After the dowork event handler is executed, it determines whether the doworkeventargs is in the event handler. the cancel attribute is set. If the user calls the cancelasync method, doworkeventargs. cancel will be set to true. When the event handler completes normal execution, it can start from doworkeventargs. result: The execution result is obtained. If a processing exception occurs, the exception is thrown. all required information is included in the runworkercompletedeventargs instance, and asyncoperation is executed. the postoperationcompleted method generates the runworkercompleted event. Therefore, in the runworkercompleted event handler, you can obtain information about the cancellation operation, exception handling, or handling result.
Similar to the Occurrence Mechanism of runworkercompleted events, the reportprogress method is used for Asynchronous Operation progress notification events.
Public void reportprogress (INT percentprogress, object userstate)
{
If (! This. workerreportsprogress)
{
Throw new invalidoperationexception (Sr. getstring ("backgroundworker_workerdoesntreportprogress "));
}
Progresschangedeventargs Arg = new progresschangedeventargs (percentprogress, userstate );
If (this. asyncoperation! = NULL)
{
This. asyncoperation. Post (this. progressreporter, ARG );
}
Else
{
This. progressreporter (ARG );
}
}
The caller reports the progress by calling the reportprogress method in the dowork event processing program, and internally reports the progress through asyncoperation. the post method generates a progresschanged event. If asyncoperation is null, The progressreporter method is called to generate an event. However, the progressreporter method is called to generate an event, because the thread where the event is generated is the same as the dowork event, the progresschanged event handler will also execute in the same context of the dowork thread, therefore, when you access the progressbar control in the progresschanged event handler, the "Inter-thread operation is invalid: access it from a thread that is not creating the" progressbar1 "control." . I believe that this processing is a component bug. If asyncoperation is null, it is better to throw an exception or handle it without notice. It is worth mentioning that there will be no "invalid inter-thread operation" exception in testing the progressreporter method in the console application.
Combined with constructors, the following code helps you better understand the progresschanged event and runworkercompleted event generation mechanism.
Public backgroundworker ()
{
This. threadstart = new workerthreadstartdelegate (this. workerthreadstart );
This. operationcompleted = new sendorpostcallback (this. asyncoperationcompleted );
This. progressreporter = new sendorpostcallback (this. progressreporter );
}
Private void progressreporter (Object Arg)
{
This. onprogresschanged (progresschangedeventargs) Arg );
}
Private void asyncoperationcompleted (Object Arg)
{
This. isrunning = false;
This. cancellationpending = false;
This. onrunworkercompleted (runworkercompletedeventargs) Arg );
}
Finally, let's take a look at the implementation of the runworkerasync method and cancelasync method.
Public void runworkerasync (Object argument)
{
If (this. isrunning)
{
Throw new invalidoperationexception (Sr. getstring ("backgroundworker_workeralreadyrunning "));
}
This. isrunning = true;
This. cancellationpending = false;
This. asyncoperation = asyncoperationmanager. createoperation (null );
This. threadstart. begininvoke (argument, null, null );
}
Public void cancelasync ()
{
If (! This. workersuppscanscancellation)
{
Throw new invalidoperationexception (Sr. getstring ("backgroundworker_workerdoesntsuppcancancellation "));
}
This. cancellationpending = true;
}
Conclusion
The backgroundworker component simplifies event-based Asynchronous Operation Programming. Based on its implementation principle, you can further compile Asynchronous Operation components supporting multiple tasks to better meet the intensive application development requirements of asynchronous operations.
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/zhzuo/archive/2008/07/23/2699305.aspx