Home c# Threads, Task'i, async'i, awaits in C # under WPF on .NET Framework...

Threads, Task’i, async’i, awaits in C # under WPF on .NET Framework 4.8

Author

Date

Category

Start would like to @effetto with Hubra

thread (thread, stream) – is a processor flow encapsulation. This is a software wrapper over the functionality of your system. Task (task) – is a unit of work that can be performed in parallel. The use of one or another tool depends on your tasks. For example, if you need to perform operations in the background, your choice is a flow. If you need to parallel to perform calculations or wait for an asynchronous operation (for example, I / O), then your choice is the task.

If you believe it says, then why not use Task to perform operations in the background? After all, they themselves are scattered in streams and make a task in the background and bring the answer.

leafed further and stumbled upon this post: What is the difference between Task and Thread and when what better to use? ,
in which @vladd says that

Thread is a physical, system execution stream (with the exception of SQL Server under .NET 2.0, yes). And Task is a thing that essentially jumps out of the flow into the stream, and often it is not at all in any stream!

For the current version of the language it makes sense to almost always prefer Task and avoid Thread’ov, they are too low. Use Task’ami, they know how much more.

And then he says

No need to do anything manually, Thread Pool will take care of the distribution of downloads between the cores. C # – High-level language, system-dependent details in it are solved automatically.

Here I have dissonance. It turns out thread ‘s are not connected with the nuclei, and the load on the kernel spreads another mechanism.
Then why are it necessary to thread , if there is a magic Thread Pool , which will do everything for you, give only Task OS for processing. So besides, Tread ‘s are also isolated and not to get a response from them.

So more async and await hooked. What they imagine and could not understand. The code with ASYNC looks like a pseudo-multi-threaded code, but for some reason, it is performed in different threads. I do not understand.

Can you explain how to build a hierarchy in your head or a system of connections to understand thread ‘s, task ‘ and, async And await ?

That’s because of what task I break my head:
There is a WPF application that has an interface with an error display. There are some cyclic functions that need to be made in the background. If something went wrong in some kind of function, it is necessary to spawn an error in the interface and restart the cyclic function, or start another parallel function, or do anything.

Thread I used, you can say, Naobum, as a temporary solution to the problem of multithreading, when UI depended on the cyclic function.

Public Partial Class Mainwindow: Window
{
  MyProcessor P = New MyProcessor ();
  Public Mainwindow ()
  {
    Initializecomponent ();
    p.start ();
  }
}
Public Class MyProcessor
{
  Private Bool Somethingiswrong;
  Private Thread Thread = NULL;
  Public Void Start ()
  {
    Stop ();
    Thread = New Thread (LOOP);
    thread.start ();
  }
  Public Void Stop ()
  {
    If (Thread! = NULL & amp; & amp; thread.isalive)
    {
      thread.Abort ();
      thread = null;
    }
  }
  Private void loop ()
  {
    While (True)
    {
      IF (Somethingiswrong)
      { 
Stop ();
        // Spread the parent error (or in the main stream of the GUI)
      }
      // Do Something.
    }
  }
}

How do I rewrite this code for the option Spread your parent error ? Use ASYNC / AWAIT ? Or Task ?

Is it possible to use TASK like THREAD ‘in simple way? When I only learned about them, the feeling that was possible, but as soon as I began to dig deeper, it turned out that it seems to be no …

Maybe Thread This is a kind of containers for Task ‘s?


Answer 1, Authority 100%

To begin with, there are system thread ‘s (I will call them streams, okay?) – This is a low-level fitting of the operating system, which is parallel to the executing code. The operating system streams are running in parallel, and to communicate with each other, for example, the overall memory of the process.

Now, the .NET does not provide direct access to system threads *, and determines its own. These streams are usually built on the basis of systemic (this is not guaranteed, but in most cases it is so).

What should I need .NET-Whelmed threads. Well, for example, the .NET is not obliged to run on a Windows platform, and it would be very uncomfortable to write cross-platform code if on each system would have to work with them in its own way.

In any case, the streams, both system and .NET-oski, provide a very simple functionality: the launch of this Void function is parallel to the rest of the program, as well as blocking the expectation of the end of this function itself.

Why is such a primitive in .NET? The fact is that historically programmers are accustomed to working with flows, so it would not provide them with this opportunity, it would be not good, and other means for asynchronism was not so much.

Next, Thread Pool (Poolflow Pool). This is a .NET feature (Windows for native applications has its own pool of streams, whether the .NET implementation is based on Windows, I do not know). It contains a set of already running streams inside, and provides the functionality “Perform this Void function on the stream that is currently free.” If there are no free streams, the task is queued. **

What is it needed for what it is, is it not necessary to do in streams? The fact is that the launch of a new stream (especially on Windows) is a fairly expensive operation, and the pool allows it to be avoided.

Next, Task . This is a completely different logical level. Task is a task that will ever be executed *** and makes a value or emissions. How the task is done, it does not matter: it can be performed in the form of a function in another stream, it may not be performed anywhere, but simply wait for the occurrence of a certain event (for example, data acquisition on the network), it can simultaneously be performed in several streams from which The results will be collected – Task can be anyhow.

Task **** provides the following functionality:

  • When the task is completed, report on the result or failure
  • in Async functions wait for asynchronously performing this Task or Task group, and transfer its result or exception to the expectation point
  • In the usual, synchronous function, wait for Task execution synchronously (however, it is recommended to use async functions in this case)
  • run your (not necessarily void-) function as Task on the stream pool or in a new stream.
  • Pack Other Asynchronous Patterns (Iasyncresult, Begin + Completion Event, Wait Handle) in Task
  • Task has a standard mechanism of cooperative breakage using CancellationToken’s
  • to Task You can connect the continuation – another task, which will be completed at the end of this (optional – with success / failure / cancel)
  • and finally the most important thing – Task can be created using async functions that make it possible to easily design from the available Task and ordinary language primitives like for / while / if other, much more complex Task.

As you can see, Task’i in terms of opportunities are much richer streams.

Now, Async / await . These pieces work as follows. ASYNC keyword announces as it would be a function that manufactures Task, and may be asynchronously waiting for other tasks asynchronously with the help of the Await keyword. Thus, we can easily combine various tasks with the most complex ways. (This is here Disassemble an example.)

Next, about long background tasks. These same tasks would still be good to be able to serve – wait when they are completed, get the result (at least successfully or not), so Task is also a natural choice. If your long task in fact does nothing most of the time (and so usually happens), you will not take a valuable stream during waiting. If your background task is all the time anything busy, then not to take a long stream from the pool for a long time, you can send it to a new thread.

Well, your code in WPF I would make it as follows.

class myprocessor
{
  Public Task Work (Iprogress & LT; errorDescription & gt; Progress, CancellationToken CT) = & gt;
    Task.Run (WorkInternal (Progress, CT));
  ASYNC Task WorkInternal (Iprogress & LT; Errordescription & GT; Progress, CancellationToken CT)
  {
    While (! Ct.iscancellationRequested)
    {
      Try.
      {
        // do something, for pauses to use await task.delay (..., ct)
      }
      Catch (OperationCancelleDexception Ex) When (Ex.cancelllationToken == CT)
      {
        // All is well, we demand from us, we go out
        Break;
      }
      Catch (other possible exceptions)
      {
        progress.report (here information about what went wrong);
        // restore the next cycle to continue
      }
    }
  }
}

On the WPF side you write just

CancellationTokensource CTS = new ();
Task Worktask = MyProcessor.Work (
    CTS.Token,
    NEW PROGRESS & LT; ErrorDescription & GT; (errorinfo = & gt; {display an error in ui}));

Callback with ErrorInfo will automatically come in the desired stream.

At the end of the work, do not forget to stop asynchronous work with

cts.cancel ();
await worktask;

* Well, the ok, it gives, if via P / INVOKE, but this is a low-level tool for calling OS functions and other unmanaged libraries.

** In fact, there is a little cunning, the flow pool can create additional streams, if he sees that it has accumulated enough tasks, and destroy streams if they are idle without work.

*** In other languages, such a thing is often called Future or Promise .

**** More precisely, TPL library and async / await functionality.


Answer 2, Authority 39%

I will try simple words.

Thread – this thing performs work. You will fard her code, she chews it. Just as your main code is performed, including the one that creates and starts a new stream. In general, any code is performed inside the Thread , and it cannot be done anywhere else. That is, this is a physical entity necessary to perform code. Stope when you run a lot of threads to perform as much code as possible per unit of time called multithreading .

Task – This thing that gives you the opportunity to wait. What to wait: for example, I / O operations. For example, you sent a TCP request and wait. This code is performed in some thread while you are waiting? With great probability, without deepening in the details – definitely yes. But this thread is not on your computer, but on the server, and your computer is waiting for the package to give you the data and run the processing method from the place where it stopped when you sent a package. Waiting for something running “somewhere there”, called asynchrony .

While you are waiting for a single stream at all, nowhere else. For example, the hard disk controller can look for you the desired file to give you the data, in this place the processor can not do anything at all and do not execute your code. In other words, to perform an asynchronous operation can be used from 0 to 100,500 threads. One big asynchronous operation may consist of many small, for each small can be used from 0 to many streams, etc.

What can I want to wait at once.

await myshinycoolmethodasync ();

And you can not immediately

task task = myshinycoolmethodasync ();
Console.WriteLine ("It is already running!");
Await Task;
Console.Writeline ("It finally ended.");

And you can even wait for a bunch of running asynron operations at once.

list & lt; task & gt; Tasks = New List & lt; Task & gt; ();
for (int i = 0; i & lt; 100500; i ++);
{
  tasks.add (myshinycoolmethodasync ());
}
Await Task.whenall (Tasks);

and many more things can be. And you can (but this is not exactly) to implement these 4 lines of code written above, but only with threads (not trapping, namely, the same behavior in the emission of exceptions, return results and other charities Async ). I would notice the row 500, at best, to make it easier to make a naked Thread .

What else can wait task – for example, when some code will work in another stream. TPL library API (Task Parallel Library) provides an opportunity to easily run the code in another stream.

task task = task.run (() = & gt; console.writeline ($ "hello from the flow number {thread .CurrentThread.managedthreadid} "));

That is, you can guess that this task.run inside itself creates Thread , and puts the code there. Quite right.

Only the creation of the stream is the thing is dear, eats the company’s resources, and to save, the streams start not for each method call, but only when you need and reuse them. That is, you launched 1 method – a stream was created, the code was executed, the flow was freed. In when you after the completion of the first, you run the second method, then the previously created flow can be reserved for it, if it is free. This is called pool streams.

task.run by default uses the stream pool, but you can twist, so as not to use, and spawned a separate personal stream, and you will be forced to wait ~ 30 milliseconds until it happens (the same execution thread.start () occurs, the same delay). For comparison, the time of reversing the free flow from the pool ~ 1-2ms. Actually, the poor performance of multi-threads is the main problem that the flow pool decides.

Asynchronous code in C # can write differently, but the easiest way is the help of two keywords async / await . The first creates a state machine from the method (I will not chew, the topic googles easily), the second allows you to wait for the completion of the Tack and translates this very machine of states from one state to another. That is, continues to perform a single whole method, but actually cut into pieces in those places where you used await .

AWAIT Without ASYNC use in methods will not work. Well, not to change the state of the state of the states, if not the machine itself. async Without AWAIT You can use it, but it is useless, and it looks like if you bought a car, I washed her, they were polled by fuel, and did not go anywhere. “Not cool”, you think. Here and the processor of your computer will also think if you use ASYNC without AWAIT . Therefore, only together, and in no way.


Your code, asynchronous.

Immediately remove the code from the designer, because it is impossible to fasten async to it, and move to the event handler window.loaded .

Public Partial Class Mainwindow: Window
{
  Public Mainwindow ()
  {
    Initializecomponent ();
  }
  Private Async Void Window_Loaded (Object Sender, RouteDeventArgs E)
  {
    Try.
    {
      MyProcessor P = New MyProcessor ();
      await p.startasync ();
    }
    CatchWENTWRONGEXCEPTION EX)
    {
      MessageBox.Show ($ "Hurray! {Ex .Message}");
    }
    Catch (Exception Ex)
    {
      MessageBox.Show ($ "Everything broke: {ex .Message}");
    }
  }
}
Public Class MyProcessor
{
  Private Bool Somethingiswrong;
  Private CancellationTokensource CTS;
  Public Async Task Startasync ()
  {
    if (CTS! = NULL)
      Return;
    Try.
    {
      Using (CTS = New CencelllationTokensource ())
      {
        AWAIT LOOPASYNC (CTS.Token);
      }
    }
    Finally
    {
      CTS = NULL;
    }
  }
  Public Void Stop ()
  {
    CTS? .cancel ();
  }
  Private Async Task Loopasync (CancellationToken Token)
  {
    While (! Token.IscancellationRequested)
    {
      await task.delay (100500, token); // Almost all methods know how to take token cancellation
      IF (Somethingiswrong)
      {
        Throw New SomethingWentwrongException ("Something went wrong");
      }
      // Do Something
      token.throwifcancellationrequested (); // Instead of checking iscancellationrequested, you can throw an exception if canceled
    }
  }
}

Documentation .

Programmers, Start Your Engines!

Why spend time searching for the correct question and then entering your answer when you can find it in a second? That's what CompuTicket is all about! Here you'll find thousands of questions and answers from hundreds of computer languages.

Recent questions