Article From:https://www.cnblogs.com/zhlziliaoku/p/9217225.html

First, introduction

  In the last topic, we introduced the asynchronous programming model proposed in.NET 1, APM, which, although APM provides some support for our asynchronous programming, also has some obvious problems – not supporting the cancellation of asynchronous operations and providing no work on progress reports.Yes, support for progress reporting and cancellation is also essential for an interface – based application. Since there is such a problem, Microsoft should of course provide us with a solution to the problem, so Microsoft provided us with a new asynchronous programming model at.NET 2.That is the event based asynchronous programming model introduced in my topic, EAP.

       A class that implements an event based asynchronous mode will have one or more methods and corresponding Completed events with Async as a suffix, and these classes support the cancellation of the asynchronous method, the progress report, and the report results.

       When we call the XxxAsync method that implements the class of asynchronous mode based on events, that is, the representative starts an asynchronous operation, and after that method, a thread pool thread is made to perform the time-consuming operation, so when the UI thread calls the method, it will certainly not plug the UI thread.

Two. Deep analysis of BackgroundWorker component classes

Before we go deep into the BackgroundWorker class, let’s take a look at the members and the corresponding introduction of the BackgroundWorker class (here are a list of the attributes and methods that are often used in asynchronous programming, specifically about the class members that can view the MSDN – BackgroundWorker):

BackgroundWorkerclass

Public property

Attribute name

Explain

CancellationPending

Gets a value indicating whether the application has requested to cancel the background operation.

IsBusy

Gets a value indicating whether BackgroundWorker is running an asynchronous operation.

WorkReportsProgress

Gets or sets a value indicating whether BackgroundWorker can report progress updates.

WorkerSupportsCancellation

Gets or sets a value indicating whether BackgroundWorker supports asynchronous cancellation.

Public methods

Name

Explain

CancelAsync

Request to cancel the hanged backstage operation.

ReportProgress

Triggering the ProgressChanged incident (official explanation?) )

RunWorkerAsync

Start the backstage operation.

Public events

Name

Explain

DoWork

When you call RunWorkerAsync, this is what the official explains. Do you want to know why the RunWorkerAsync method triggers DoWork events?

ProgressChanged

When you call ReportProgress, this is what the official explains. Do you want to know why the ReportProgress method triggers ProgressChanged events? )

RunWorkerCompleted

Occurs when the background operation has been completed, cancelled, or thrown an exception.

Why do we call the RunWorkerAsync method triggering DoWorker events?

// RunWorkerAsyncThe source code didn't do anything, but called the overload method RunWorkerAsync (objectargument) method.
publicvoidRunWorkerAsync()

{

    this.RunWorkerAsync(null);

}

View Code

// Let's take a look at the source code of RunWorkerAsync's overloaded method with one parameter.

publicvoidRunWorkerAsync(object argument)

{

    if (this.isRunning)

    {

        thrownewInvalidOperationException(SR.GetString("BackgroundWorker_WorkerAlreadyRunning"));

    }

   //This method assigns some private fields//These assignments are for us to use isBusy common properties to check whether BackgroundWorker components are running asynchronous operations.//And check the common attribute CancellationPending attribute to check whether the asynchronous operation is cancelled.

    this.isRunning=true;

    this.cancellationPending=false;

 

    //AsyncOperationClass is through the synchronization of the calling thread to achieve cross - thread access, the implementation of the APM topic in our own code to achieve, but the implementation of the EAP class in the content to help us, so that we do not need to solve the problem of ourselves, from which can also see the real EAPNow it is based on APM, but the realization of EAP helps us do more things behind it.

    this.asyncOperation= AsyncOperationManager.CreateOperation(null);

 

    //Here is the asynchronous programming part introduced in our previous topic using delegate.// We call the BeginInvoke method in the class of EAP, which can also prove that EAP is based on APM, so the introduction of APM is necessary.

    this.threadStart.BeginInvoke(argument,null,null);

}

View Code

1.    We can see from the code above that calling the RunWorkerAsync method is to call the threadStart delegate, and we want to know what happened behind the RunWorkerAsync method, and the first need to know the threadStart CommitteeWhich way is the packing? And we need to know where the delegations are instantiated.2.    Where does the entrustment be instantiated? When it comes to instantiation, of course, the first thing you think of is the constructor. OK, let's look at the BackgroundWorker constructor:// View constructors here are all due to previous analysis.// From the constructor, we can see that the threadStart delegate is initialized here.
public BackgroundWorker()
{
    // Initialization of threadStart delegate
    this.threadStart=new WorkerThreadStartDelegate(this.WorkerThreadStart);
    // We also initialize the operation to complete the delegate and progress report delegate.
    this.operationCompleted= new SendOrPostCallback(this.AsyncOperationCompleted);
    this.progressReporter=new SendOrPostCallback(this.ProgressReporter);
}
3. As you know from the constructor that threadStart has wrapper the WorkerThreadStart method to solve the first doubt, let's look at the code of the WorkerThreadStart method:PrivatevoidWorkerThreadStart (object argument)
{
    objectresult =null;
    Exceptionerror =null;
    boolcancelled =false;
    try
    {
       DoWorkEventArgs e =newDoWorkEventArgs(argument);
       //The onDoWork method is also called in this method//
        this.OnDoWork(e);
        if(e.Cancel)
        {
           cancelled =true;
        }
        else
        {
           result = e.Result;
        }
    }
    catch(Exception exception2)
    {
        error= exception2;
    }
    //It is also explained that the Completed event will be triggered when the operation is completed.// The analysis process and invoking the RunWorkerAsync method trigger DoWork events similar.
    RunWorkerCompletedEventArgs arg =newRunWorkerCompletedEventArgs(result, error, cancelled);
    this.asyncOperation.PostOperationCompleted(this.operationCompleted,arg);
}

4. In the above code, you can know that WorkerThreadStart calls the protected OnDoWork method, and let's look at the code of the OnDoWork method, where we are not far from the nature of the thing.// OnDoWorkSource code
protectedvirtualvoidOnDoWork(DoWorkEventArgs e)
{
    //Obtaining the delegate object from the event set
   DoWorkEventHandler handler = (DoWorkEventHandler)base.Events[doWorkKey];
    if(handler !=null)
    {
        //Invoking a delegate is the way to invoke the DoWork event.// When we use the BackgroundWorker object, we first need to register its DoWork event.//Here you can explain why calling the RunWorkerAsync method triggers the DoWork event.
       handler(this, e);
    }
}
// When we use the + = symbol to register the DoWork event, we invoke the Add method behind it.
publiceventDoWorkEventHandler DoWork
{
    add
    {
        //Add the name of the registration method to an event set.// The collection of events is also like a dictionary. DoWorkKey is the key of the registration method. Through this key, we can get the delegate of the packaging registration method.
        base.Events.AddHandler(doWorkKey,value);
    }
    remove
    {
        base.Events.RemoveHandler(doWorkKey,value);
    }
}
From the annotations in the above code, we can explain the beginning of the puzzle, and better explain the event characteristics. As for events, you can also see my event topic.It can be understood repeatedly according to the following characteristics.As for the analysis of several other puzzles in the beginning of the form, similar to this analysis, you can understand the BackgroundWorker class in a deep understanding of this idea, and I don't explain much here. Believe in bigMy family can solve several other doubts soon through my analysis. If you fully understand the above analysis, you will have a further understanding of the EAP, the delegate and the event.Four. Asynchronous programming using BackgroundWorker components.Dissection of BackgrouAfter the ndWorker component, do we really want to see how to use this class to implement asynchronous programming? Let's show you a small program that uses BackgroundWorker components to download files asynchronously, which supports asynchronous downloading (referring to a thread pool)Through this program, we believe that you will also have a better understanding of the event based asynchronous mode and what kind of tasks the pattern can do. Here are the main codes of the program (because the code is all in the code)With detailed explanations, there is little explanation for code implementation here.//Begin Start Download file or Resume the download
        privatevoidbtnDownload_Click(object sender, EventArgs e)
        {
            if(bgWorkerFileDownload.IsBusy !=true)
            {
                   // Start the asynchronous operation
                   // Fire DoWork Event
                   bgWorkerFileDownload.RunWorkerAsync();          
                   // Create an instance of the RequestState
                   requestState =newRequestState(downloadPath);
                   requestState.filestream.Seek(DownloadSize, SeekOrigin.Begin);
                   this.btnDownload.Enabled =false;
                   this.btnPause.Enabled =true;
            }
            else
            {
               MessageBox.Show("The operation is being performed, please later");
            }
        }
        //Pause Download
        privatevoidbtnPause_Click(object sender, EventArgs e)
        {
            if(bgWorkerFileDownload.IsBusy&&bgWorkerFileDownload.WorkerSupportsCancellation==true)
            {
               // Pause the asynchronous operation
               // Fire RunWorkerCompleted event
               bgWorkerFileDownload.CancelAsync();
            }
        }
        #regionBackGroundWorker Event
        //Occurs when RunWorkerAsync is called.
        privatevoidbgWorkerFileDownload_DoWork(object sender,DoWorkEventArgs e)
        {
            // Getthe source of event
            BackgroundWorker bgworker = senderasBackgroundWorker;
            try
            {
               // Do the DownLoad operation
               // Initialize an HttpWebRequest object
               HttpWebRequest myHttpWebRequest =(HttpWebRequest)WebRequest.Create(txbUrl.Text.Trim());
               // If the part of the file have been downloaded,
               // The server should start sending data from the DownloadSize to the endof the data in the HTTP entity.
               if (DownloadSize !=0)
               {
                   myHttpWebRequest.AddRange(DownloadSize);
               }
               // assign HttpWebRequest instance to its request field.
               requestState.request = myHttpWebRequest;
               requestState.response =(HttpWebResponse)myHttpWebRequest.GetResponse();    
               requestState.streamResponse = requestState.response.GetResponseStream();
               int readSize =0;
               while (true)
               {
                   if (bgworker.CancellationPending ==true)
                   {
                       e.Cancel = true;
                       break;
                   }
                   readSize = requestState.streamResponse.Read(requestState.BufferRead,0,requestState.BufferRead.Length);
                   if (readSize >0)
                   {
                       DownloadSize +=readSize;
                       intpercentComplete = (int)((float)DownloadSize/ (float)totalSize * 100);
                       requestState.filestream.Write(requestState.BufferRead,0,readSize);
                       // Report progress, triggering the ProgressChanged incident
                       bgworker.ReportProgress(percentComplete);
                   }
                   else
                   {
                       break;
                   }
               }
            }
            catch
            {
               throw;
            }
        }
        //Occurs when ReportProgress is called.
        privatevoidbgWorkerFileDownload_ProgressChanged(object sender,ProgressChangedEventArgs e)
        {
            this.progressBar1.Value= e.ProgressPercentage;
        }
        //Occurs when the background operation has completed, has been canceled, or hasraised an exception.
        privatevoidbgWorkerFileDownload_RunWorkerCompleted(objectsender, RunWorkerCompletedEventArgs e)
        {
            if(e.Error !=null)
            {
               MessageBox.Show(e.Error.Message);
               requestState.response.Close();
            }
            elseif(e.Cancelled)
            {
                MessageBox.Show(String.Format("Download the pause, download the file address: the number of bytes that {0}\n has downloaded is: {1} bytes.",downloadPath, DownloadSize));
                requestState.response.Close();
                requestState.filestream.Close();
                this.btnDownload.Enabled =true;
                this.btnPause.Enabled =false;
            }
            else
            {
               MessageBox.Show(String.Format("Download has been completed, download the file address: {0}, the total number of bytes of the file is {1} bytes.",downloadPath, totalSize));
               this.btnDownload.Enabled =false;
               this.btnPause.Enabled =false;
               requestState.response.Close();
               requestState.filestream.Close();
            }
        }
        #endregion
        // GetTotal Size of File
        privatevoidGetTotalSize()
        {
            HttpWebRequest myHttpWebRequest =(HttpWebRequest)WebRequest.Create(txbUrl.Text.Trim());
            HttpWebResponse response =(HttpWebResponse)myHttpWebRequest.GetResponse();
            totalSize = response.ContentLength;
            response.Close();
        }
 //This class stores the State of the request.
    publicclassRequestState
    {
        publicintBufferSize =2048;
        publicbyte[]BufferRead;
        publicHttpWebRequest request;
        publicHttpWebResponse response;
        publicStream streamResponse;
        publicFileStream filestream;
        publicRequestState(string downloadPath)
        {
           BufferRead =newbyte[BufferSize];
           request =null;
           streamResponse =null;
           filestream =new FileStream(downloadPath,FileMode.OpenOrCreate);
        }
    }

 

Leave a Reply

Your email address will not be published. Required fields are marked *