Multi-threaded learning (1) - User-based users based on multi-thread-based applications

xiaoxiao2021-03-13  188

Why choose multithreaded?

Multi-threading can keep you keeping you by ensuring that the program is "never sleep"

UI

Quick response.

Under multithreading, a longer task can be run in its own thread, which is often referred to as auxiliary thread. Because only the auxiliary thread is blocked, the blocking operation no longer causes the user interface to freeze.

The basic principle is that the thread is responsible for responding to the user input and maintaining the user interface (usually called

UI

Threads should not be used to perform any time consuming long operation. It is used to practice, any time consuming

30ms

Operation should be considered

UI

Remove it in the thread.

If you want to keep the user interface, any blocking operation should be performed in the auxiliary thread.

-

Whether it is mechanical waiting for something (for example, waiting

CD-ROM

Start or hard disk positioning data, or wait for a response from the network.

Asynchronously commissioned

The easiest way to run code in the auxiliary thread is to use asynchronous commission (all delegates provide this feature). The delegation is usually called in synchronous mode, ie, when the delegate is called, only the call is returned after returning. To call the delegate in asynchronous mode, please call

BeginInvoke

Method, this method will be queued to run in the thread of the system thread pool. The calling thread will return immediately without waiting for the method to be done. This is more suitable for

UI

Program, because it can be used to start a longer job, and the user interface is slowed slowly.

In the following code,

System.windows.Forms.MethodInvoker

Type is a system definition for a method for calling without parameters.

Private void startsomeworkfromuteread () {

// the work we want to do is to slow for the ui

// Thread, So Let's Farm It Out to a worker thread.

MethodInvoker Mi = New MethodInvoker

RunsonWorkerthread;

Mi.BeginInvoke (NULL, NULL); // this will not block.

}

// The Slow Work Is Done Here, ON A Thread

// from the system threeread pool.

Private void RunsonWorkerthread () {

DOSMETHINGSLOW ();

}

If you want to pass parameters, you can choose the principal-defined delegate type, or you can define the delegate.

Calling BeginInvoke will cause the method to run in the thread of the system thread pool, without blocking the UI thread so that other operations can be performed.

If you need the result returned by this method, the return value of BeginInvoke is important and you may not pass empty parameters.

However, for most UI applications, this style of "no matter" is most effective.

It should be noted that BeGinInvoke will return an IASYNCRESULT. This can be used with the delegated endInvoke method to retrieve the call results after the method is called.

Threads and controls

Windows

The most important thread rule in the form: In addition to the very few exceptions, do not use any members of the control in the threads other than the thread. The result of the rule is an included control (such as a button included in a form) must be in the same thread with the included control bits. That is, all controls in one window belong to the same

UI

Thread. most

Windows

The form application eventually has only one thread, all

UI

Events happen on this thread. This thread is often referred to as a UI

Thread. This means that you cannot invoke any of the methods on any control in the user interface, unless you can call it in the document description of the method.

Note that the following code is illegal:

// Created on ui thread

PRIVATE LABEL LBLSTATUS;

...

// Doesn't Run on Ui Thread

Private void RunsonWorkerthread () {

DOSMETHINGSLOW ();

LBLSTATUS.TEXT = "Finished!"; // bad !!

}

This is the main problem in multi-threaded errors, that is, they do not immediately appear. Even when there are some errors, everything looks normal before the first demo program.

Call the control in the correct thread

In theory, you can use low-level synchronization principles and pool technology to generate your own mechanism, but fortunately, because there is one

CONTROL

Category

Invoke

method

The existing solution is not required to use such a low-level way.

The Invoke method is one of several thread rules for document records in the Control class: it can always invoke CONTROL from any thread. The Invoke method itself simply carries a delegate and an optional parameter list, and calls you in the UI thread without considering which thread is sent by the invoke call. In fact, get any way for controls to run on the correct thread. However, it should be noted that this mechanism is only valid only when the UI thread is currently blocked - calls only when the UI thread is ready to process user input. Invoke method will test to understand whether the call thread is the UI thread. If so, it directly calls the delegate. Otherwise, it will arrange the thread switching and adjust the delegate on the UI thread. No matter which case, the approach packaged will run in the UI thread, and INVOKE will only return only when the method is completed.

The Control class also supports the asynchronous version of Invoke, which will return and arrange the method to run on the UI thread at a time in the future. This is called BeGinInvoke, which is very similar to asynchronous commission, and the obvious difference between the commission is that the delegation is running in an asynchronous manner to run on a thread pool in an asynchronous manner. BeginInvoke runs asynchronously on the UI thread. Control's Invoke, BeginInvoke, and EndInvoke methods, and invokerequid attributes are members of the IsynchronizeInvoke interface. This interface can be implemented by any class that requires controlling its event delivery. Since BeGinInvoke is not easy to cause dead locks, it is used as much as possible; and uses less invoke methods. Because INVOKE is synchronized, it will block the secondary thread until the UI thread is available.

Review the previous code. First, a delegate must be passed to the policy of the Control to run the thread sensitive code on the UI thread. This means that the code should be placed in its own method. (The legal version of the code snippet shown in front)

// Created on ui thread

PRIVATE LABEL LBLSTATUS;

•••••

// Doesn't Run on Ui Thread

Private void RunsonWorkerthread () {

DOSMETHINGSLOW ();

// DO UI Update on Ui Thread

Object [] plist = {this, system.eventargs.empty}; lblstatus.beginvoke

New System.EventHandler (Updateui), PLIST

}

•••••

// Code to Be Run Back on the ui thread

// (Using System.EventHandler Signature

// sowe don't need to define a new

// delegate Type Here)

Private Void UpdateUi (Object O, System.EventArgs E) {

// Now OK - this Method Will Be Called Via

// Control.Invoke, So We are allowed to do

// Things to the ui.

LBLSTATUS.TEXT = "Finished!";

}

Once the auxiliary thread completes the slow work, it will call the BeginInvoke in Label to run a certain piece of code on its UI thread. Through this, it can update the user interface.

Packaging Control.Invoke

If the auxiliary thread wants to provide more feedback information at the end, not simply give "Finished!", BeginInvoke is too complicated to use. In order to communicate other messages, such as "processing", "everything goes", etc., you need to see a parameter to the UpdateUI function. You may also need to add a progress bar to increase feedback. So multiple calling BeGinInvoke may cause the auxiliary thread to be dominated by the code. This will not only cause inconvenience, but also take into account the coordination of the auxiliary thread and the UI, which is not good. How to do it? Use the packaging function! Based on the above requirements, the above code improvements are as follows:

Public class myform: system.windows.Forms.form {

...

Public void showprogress (string msg, int init percentdone) {

// Wrap the Parameters in Some Eventargs-Derived Custom Class:

System.Eventargs E = New MyProgRessevents (MSG, Percentdone);

Object [] plist = {this, e};

// invoke the method. This class is deived

// from form, sowe can Just Call BeginInvoke

// TO GET to the ui thread.

BeginInvoke (New MyProgressSeventshandler (Updateui), PLIST);

}

Private delegate void myprogresseventshandler

Object sender, MyProgressEvents E);

Private Void UpdateUi (Object Sender, MyProgressEvents E) {

LBLSTATUS.TEXT = E.MSG;

MyProgressControl.Value = E.PERCENTDONE;

}

}

Here, you define your own method, which violates the rule of "Must invoke on the UI thread" because it only calls other methods that are not subject to this rule constraint. This technique will lead to a more common topic: Why not write public methods on the control (these methods are the exceptions of the UI thread rule)? Just the Control class provides a useful tool for this method. If I provide a public method designed to call from any thread, it is entirely possible that someone will call this method from the UI thread. In this case, it is not necessary to call BeGinInvoke because I am in the correct thread. Calling Invoke is completely wasting time and resources, it is better to directly call the appropriate method. In order to avoid this, the Control class will open a property called InvokeRequired. This is another exception to the "UI thread" rule. It can read from any thread. If the calling thread is a UI thread, return false, and other threads return true.

Public void showprogress (string msg, int init percentdone) {

IF (InvokeRequired) {

// AS Before

...

} else {

// We're Already on The Ui Thread Just

// Call Straight Through.

UpdateUi (this, new myprogresss (msg,

Percentdone));

}

}

ShowProgress can now be recorded as a public approach that can be called from any thread. This does not eliminate complexity - the code to execute BeginInvoke still exists, it also poses a place. Unfortunately, there is no simple way to get rid of it (depressed).

locking

If the two threads perform a write operation at the same location at the same location, all threads read data from that position will see a pile of spam after the synchronous write operation occurs. To avoid this problem, a measure must be taken to ensure that only one thread can read or write a status of an object at a time. Preventing these problems from using the way to use the runtime lock function. C # allows you to use these features to protect the code by locking keywords (Visual Basic also has a similar construct, called SyncLock). The rule is that any object you want to call its method in multiple threads (whether reading or writing) is used each time (whether reading or writing) is accessed.

Still see an example:

// this Field Could Be Modified and Read on Any Thread, So All Access

// must be protected by locing the object.

PRIVATE DOUBLE MYPOSITION;

•••••

Public double position {

Get {

// Position Could Be Modified from Any Thread, So We next to Lock

// this Object to make Sure We get a consistent value.

LOCK (this) {

Return myposition;

}

}

SET {

LOCK (this) {

MyPosition = Value;

}

}

}

Public void moveby (double offset) {// Here you also lock

// Here We are Reading, Checking and the Modifying The value. IT IS

// vitally important this entire sequence is protected by a // single lock block.

LOCK (this) {

Double newpos = position offset;

// Check Withnin Range - MINPOS and MAXPOS

// Will Be Const Values ​​Defined Somewhere in

// this class

IF (NewPOS> MAXPOS) newpos = maxpos;

Else IF (NewPOS

Position = newpos;

}

}

When the modification is more complicated than simple reading or writing, the entire process must be protected by a separate locking statement. This also applies to updating multiple fields - must not release the lock before the object is in a consistent state. If the lock is released during the update status, other threads may be able to obtain it and see inconsistent states. If you already have a lock and call a method attempt to get the lock, it will not cause problems because the individual thread allows the same lock multiple times. This is very important for code that needs to be locked to protect low-level access to fields and high-level operations performed on the field.

Dead lock

First look at the example:

Public class foo {

Public void calbar () {

LOCK (this) {

Bar mybar = new bar ();

Mybar.barwork (this);

}

}

// this Will Be Called Back on A Worker Thread

Public void fooWork () {

LOCK (this) {

// do some work

•••••

}

}

}

Public class bar {

Public void barwork (foo myfoo) {

// Call foo on diffrent thread via delegate.

MethodInvoker Mi = New MethodInvoker

Myfoo.foowork;

IASYNCRESULT Ar = Mi.BeginInvoke (NULL, NULL);

// do some work

•••••

// now Wait for delegate Call to Complete (Deadlock!)

Mi.endinvoke (AR);

}

}

There are two or more threads being blocked to wait for the other party. The situation here is still different from standard deadlocks, and the latter usually includes two locks. This shows that if there is a causality (process call chain) exceeds the thread boundary, a deadlock occurs, even if only one lock is included!

Control.invoke

It is a way to adjust the process of threads, this is an important fact.

BeginInvoke

Will not encounter such problems because it does not make the causality across threads. In fact, it will start a new cause in a thread pool thread to allow the original independent. However, if retained

BeginInvoke

return

IASYNCRESULT

And use it to call

Endinvoke

Then there will be a problem, because

Endinvoke

There is actually two causal sects to be one. The easiest way to avoid this is that when you hold an object lock, don't wait for a cross-thread call to complete. To ensure this, avoid calling in the lock statement

Invoke

or

Endinvoke

. As a result, when holding an object lock, there will be no need to wait for another thread to complete actions. It is easy to insist on this rule and say it is easy to do. The best rule is that it is not adjusted at all.

Control.invoke

with

Endinvoke

. This is why the programming style of "startup" programming style is also why

Control.BeginInvoke

Solution is usually better than

Control.invoke

Good reasons for solutions.

As long as it is possible, it should be avoided when holding a lock, because if not, the dead lock is difficult to eliminate.

Make it simple

Here, I am still halo, there is a problem: how to make the most of the multi-thread benefit, and will not encounter a tricky error that plagues concurrent code?

UI

The nature of the code is: it receives events from an external resource, such as user input. It will handle it when an event occurs, but it will spend most of the time in the wait event. If you can construct auxiliary thread and

UI

Communication between threads makes it suitable for this model, but it is not necessarily encountered because there will be no new things introduced.

This makes things simplistic: focus the auxiliary thread as another asynchronous event source. as

Button

Control passes such as

Click

with

MouseEnter

Such an event can be auxiliary thread as a transmission event (such as

ProgressUpdate

with

Workcomplete

Something. Just simply see this as a ratio, or truly encapsulates the auxiliary object in a class and discloses an appropriate event in this way, which is entirely dependent on you. The latter choice may require more code, but it will make the user interface code even more uniform. No matter which case, you need

Control.BeginInvoke

These events are delivered on the correct thread.

For assistive threads, the easiest way is to write code as code blocks in normal sequence. But if you want to use the "will assist thread as an event source" model, what is it? This model is very applicable, but it puts a restriction on the interaction of the code and user interface: this thread can only

UI

Send messages and cannot ask it.

For example, let the auxiliary thread launched a dialogue to request information required to complete the results. If you really need this, it is best to initiate such a conversation in the auxiliary thread, not at the Lord.

UI

Start in the thread. This constraint is advantageous because it will ensure a model that is very simple and suitable for communication between two-threaded intervals.

-

It is a key to success here. This advantage of this development style is that the thread blocking will not occur when waiting for another thread. This is an effective strategy to avoid deadlocks

转载请注明原文地址:https://www.9cbs.com/read-129119.html

New Post(0)