Understanding threads in C#

I'm trying to figure out how to use Tasks and asynchronous methods in C#, but every place I see about this in internet mentions a "usual multithreading" way that would be different from using tasks.

The problem is that so far I have not understood very well how the threads work, I only know that they would intuitively be "code execution paths".

In this way, how to correctly understand threads in C#? What they are and how we work with these threads? I believe that this question is analogous to that I did, but considering now the relationship with the processor and not with the memory.

Author: Comunidade, 2014-05-06

3 answers

Using threads in C#, is semantically similar to using threads in Java, C or c++, what changes is the form (syntax) with which the threads are created/used in different languages.

Answering the question about how to use threads, in an exhaustive and general way, requires more space and time than provided here, since there are entire courses and disciplines dedicated to this.

However, these are some well-known books, used in graduation, which can give a good enlightened on the subject (there are others, but these are the most common):

  • Distributed Systems - Principles and paradigms of Tanenbaum
  • operating system basics of Silberschatz
  • modern operating systems of Tanenbaum

Now, taking advantage of the space, in a simplified way, you can imagine a thread as a snippet of code that runs on parallel to your code, and there may be several threads running at a given time.

From the moment you create a thread, the operating system knows that, in addition to everything it is already doing, there is more code that needs to be executed. If there are more threads than processors/cores (which is the case most seen on personal computers and mobile devices), then the operating system starts to "schedule a moment" for each thread to run on a certain core / processor (process known as scaling, or scheduling).

For example, assuming you have a computer with 4 cores, and the operating system has 8 threads currently running (TA, TB, TC, TD, TE, TF, TG, and TH), then a possible thread scaling might be like the one in the figure below:

Threads

Here, it can be seen that threads TA and TB share core 0, TC and TD share Core 1 and so on, so each thread uses 10 milliseconds of each core, to then "give way" to another thread that is waiting. Although there are 8 threads, only 4 are executed simultaneously on the processor, while the other 4 are waiting (without consuming processing resources).

Of course this is a simple example. In real life, scaling is not as symmetrical, and depends on a number of factors, such as thread priority (when more privilege, more processor), or even if the thread is waiting for an external event to occur, which means that it stays in the waiting State for much longer. This, by the way, is the reason why threads are so important in today's systems.

For example, imagine that in a snippet of your code you ask to write a text to a file (code in C#):

public void GravaArquivo(...) {
    //Bloco A
    File.WriteAllText(...);
    //Bloco B
}

The code in Bloco B will not be executed until the WriteAllText method finishes run. This is known as timing: an event only occurs after the previous one has completely ended. That is, from the point of view of the method GravaArquivo the execution of this code "hangs" on the line File.WriteAllText(...); until the file has been written to disk.

As brief as it may seem to us humans, this time is too large for the processor, so even the operating system leaves the thread running the GravaArquivo method in a waiting State, until the disk reports the termination of the operation. This is the external event, mentioned earlier.

This is where the creation of extra threads becomes necessary.

Imagine the scenario in the figure below, where you click the "Save" button of a program:

Save button

The Code of the "Save" button will run on the thread responsible for the entire user interface of the program, that is, as long as the execution of the Code of the Save button does not finish, it is not possible to click on another button of that program, use keyboard commands in that program, or even, update the look of the program window (all part of the user interface, as well as the Save button)!

When the user interface stops responding for a certain amount of time, operating systems begin to take action, as they have no way of knowing if the code is actually stuck (bug), or if it is just taking time. Hence the warnings of "not responding" of Windows, or that little window from Android'S ANR.

To avoid these scenarios, good programming practices require that time-consuming tasks, such as reading/writing a file, sending/receiving data over the network, etc., be performed on threads other than the thread used to process the user interface. It is at this point that you will need to create extra threads.

It is clear that there are already ready libraries and classes to facilitate this task in all modern languages, such as the Task class of C#, or even its new constructions async and await.

As much as they simplify or "disguise" the use of extra threads, the threads are there, and the semantics of use are the same. What changes is the amount of code you will have to write.

Thus, to create a thread in C#, you can use classic means, such as:

...
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        ...
        private Thread thread;
        private void CorpoDaThread()
        {
            //Código que será executado em paralelo ao resto do código
        }
        private void button1_Click(object sender, EventArgs e)
        {
            //Cria uma nova thread, indicando qual método essa thread deverá executar
            thread = new Thread(CorpoDaThread);
            //Inicia a execução da thread (em paralelo a esse código)
            thread.Start();
        }
    }
}

You can create threads using lambdas:

...
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        ...
        private Thread thread;
        private void button1_Click(object sender, EventArgs e)
        {
            //Cria uma nova thread, indicando qual método essa thread deverá executar
            thread = new Thread(() => {
                //Código que será executado em paralelo ao resto do código
            });
            //Inicia a execução da thread (em paralelo a esse código)
            thread.Start();
        }
    }
}

Or you can use Task, BackgroundWorker, async and await etc.

The important thing is to have keep in mind that in Windows used on PCs and other non-real-time operating systems, it is not possible to know with 100% certainty when a thread will actually start running. Only who knows this is the operating system.

For example, it cannot be stated with 100% guarantee that code just below the thread.Start() statement will execute either before or after of the code within that new thread being started.

That's what there are several mechanisms for synchronizing execution between concurrent threads (threads that are running in parallel), which include concepts such as Critical Section, Join, Event, Semaphore, Wait, Sleep, etc.

This subject is very extensive, and performing operations outside the user interface thread not is the only reason someone creates extra threads.

You can create threads to split the processing of a task. For example, to search for a any element in a disordered vector, if the computer can run more than one thread simultaneously, you can split the search between two threads (each searches the element in one half of the vector), so that the final time will be approx. half the time the normal search algorithm would take. This can be fieto with three, four, five or more threads.

In a game, you can split the processing of the scene between multiple threads, for example, while a thread takes care of the audio, another takes care of the network, and a third of the graphic part.

Anyway, it is a very extensive subject, but very interesting!

Mastering the concept of threads today is critical! It is not enough just to know how to use this or that class / library. I recommend going deeper into this matter, that the time spent will not be in vain: D

 38
Author: carlosrafaelgn, 2014-05-06 21:20:25

You spoke of "asynchronous". Let me ask you: do you know the technical difference from an asynchronous process to a synchronous process?

Basically, the synchronous process happens with concurrency; the asynchronous doesn't have that need by itself – I can tell you something without you expecting to hear it.

A nice example I read in the sopt meta – I don't remember the author – is the case of the phone: we say "hello?"hoping to get an answer so that just so we let's continue the conversation process; when the conversation ends, for confirmation of such a fact, we say goodbye with a "see you soon" or, even simpler, with a " bye!"–this is all to ensure that the competition is on par with the subsequent action.


threads are ways to take or split information from a source point to a destination within a stream. For example, you want to save string in memory. To do this, one or more threads are responsible so that the "save" task completes successfully.

Remember what we talked about synchronous and asynchronous?

Well, you mentioned the "multithreading" thing: this guy is asynchronous , which means you can have multiple parallel threads that divide responsibility for the tasks assigned to them. In other words, asynchronous and multiple threads are able to do operations without the need for a previously executed thread be finally finalized.

Synchronous Threads perform only one operation at a time and none are engaged while the previous one is being processed.

Piscar and escovar os dentes is quite possible and are asynchronous and independent processes. In this case, we would have a trivial example of multithreading.

Now, escovar os dentes and assoviar/assobiar operate on one thread only – one process needs to be stopped/completed before the other starts.

 8
Author: Guilherme Oderdenge, 2014-05-06 17:20:55

A Task in C#, in its simplest form, being synchronous is nothing more than a snippet of code that runs in parallel with the main stream, i.e. a thread.
Your main process or taks controller, can be another thread inclusive, can launch Tasks to perform tasks according to the demand of activity. Each Task can execute its task and terminate or can wait for more activities to run. This makes the system more efficient, as the creation process of a thread is costly, so reusing a Taks is better.
The biggest problem with using threads is having to control access to shared resources, so that only one thread has access control at a time.
Asynchronous Tasks should be used primarily with events to signal that your activity has completed.
Developing complex applications using access controls and asynchronous execution can be quite attractive, but require great experience in the development. Years of development have shown that the most efficient solutions are not those that use the most complex forms of programming.

 0
Author: lsalamon, 2014-05-06 17:16:55