Min menu

Pages

VB

Multithreading in C Plus Plus | C++

Multitasking in C-Plus Plus 

 Multitasking inC++

When you use your phone or computer, you see that you can run several programs together at the same time. Each program running in memory is consideredProcessFor example, if you run five programs together, this means that the operating system regulates the work of fiveProcessesWith some. The mechanism of running several programs together is calledMultiprocessing.

On the other hand, in the same program, you can execute several commands together and make the user feel that they are being executed simultaneously. For example, if you are playing a game such as football, you find that there are several things that happen at the same time. For example, when you play the game, you hear several Sounds (such as an enthusiastic song, the commentator's voice, the fans' voice, the referee's whistle sound in the event of a mistake, etc..), in addition to the fact that you can move the player and watch the timing of the match and many other details that all happen at the same time to make you a great game. This mechanism is called multitasking (Multithreading)Because every running part in the program is a set of commands placed inside a thread(Thread)Special.

So we use the multitasking mechanism to make the program capable of executing several commands together as if they are being executed simultaneously, and this is what we will learn in this lesson.


The importance of multitasking in C-Plus Plus

  • Make the user able to perform several operations with each other at the same time.

  • Make the application design more beautiful and add effects to it.

  • Each porridge that you run works in isolation from the rest of the commands in the program, and therefore, in the event of any error in the porridge, it will not affect the rest of the commands in the program, nor does it affect any other porridge running in the program.


Information about multitasking in C-Plus Plus

Previously, you had to rely on a library in a languageCIts name POSIso you can create a thread and deal with it because the languageC++At the time, it was not available to you.
Starting with the version C++11, everything needed to create and control a thread has been provided. Therefore, in this lesson, you will learn how to use the classes and functions in this version that work on new versions as well.

The main porridge concept inC++

inC++Every code executed in a program, must be executed in at least one thread.
That is, even if you do not put the code inside a thread, it will be placed in one.

To clarify this idea more, when the code main()placed inside the function is executed, the compiler puts it in a special thread calledMain Thread.


So when the compiler starts executing the following code it will put it inside a private thread.

The first example is in the main porridge in C-Plus

main.cpp
#include <iostream>
	  
	  using namespace std;
	  
		  // The compiler will put it inside a private thread when main() runs all the code inside the function
	  int main()
		  {
	  for(int i=1; i<=5; i++)
		  {
	  cout << i << endl;
		  }
	  
	  return 0;
		  }
	

We will get a result similar to the following when running.

1
		  2
		  3
		  4
		  5
	

Now in order to make sure that the code placed in the function main()is put by the compiler inside a thread, in the following example, we will deal with it as a thread.
For more accuracy, we will control the execution time of the code using the class this_threaddedicated to dealing with the thread that is currently running in the program.


Technical information about the main porridge in C-Plus Plus

The class this_threadcontains a fixed function whose name sleep_for()can be used to make the current porridge wait for a specified period before it continues chronoits minutes()work seconds(). milliseconds()If we want to specify the duration in milliseconds.

To use Class this_threadand Class, the two files and . chronomust be included<thread><chrono>.


In the following example, we create a loop, for each cycle it prints the counter value iand then stops for one second before moving on to the next cycle.
Note: To make the translator stop we use the function sleep_for(), and to determine the duration of the pause we use the functionseconds().

The second example is in the main porridge in C-Plus

main.cpp
#include <iostream>
	  #include <thread>
	  #include <chrono>
	  
	  using namespace std;
	  
	  int main()
		  {
		  // Then it stops for one second before moving on to the next cycle i here we create a loop that for each cycle prints the counter value
	  for(int i=1; i<=5; i++)
		  {
	  cout << i << endl;
	  this_thread::sleep_for(chrono::seconds(1));
		  }
	  
	  return 0;
		  }
	

We will get the following result when running with the indication that one number will be printed every second.

1
		  2
		  3
		  4
		  5
	

How can the translator distinguish between one thread and another?

The translator gives a tariff number(ID)Each porridge is created so that it can distinguish between them.
You can also deal with each porridge separately through its tariff number.

Principles of creating a porridge and running it inC++

In general, to create a porridge and run it, you must follow these steps.

// thread First you must include the class
	import<thread> 
	
		// callable Then we must create an object from it and pass the function we want it to execute in place of the parameter
	std::thread thread_object(callable);
	
		// At the end or before the end of the program the object must be stopped from executing so that this does not cause a problem
	thread_object.join();
  

  1. The file must be included<thread>.

  2. An object must be created from the class threadand passed a function in the parameter's place callablecontaining the commands we want to execute with this object.

  3. Parameter location callableYou can pass the name of the function you want it to execute, or define the function that it will execute directly in a methodLambdaOr pass the name of a function defined by a methodFunction Objectuntil it implements it.

Do not worry, you will learn from the examples how to create a porridge in boring detail, but always remember that these steps must be applied when dealing with porridge.



Information when creating a thread in C++

When more than one thread is running simultaneously, you cannot guarantee or specify which thread will run or expire before the other.
The reason for this is that the computer processor(CPU)It will send every thread you run to a kernel(Core)So he executes them for you at once.
And logically, the kernel that has less pressure will finish executing the porridge commands faster, and for information, a single kernel is capable of executing thousands of commands in less than one second.



note

We'll be using the function sleep_for()in most of the examples that you're going through in this lesson to make you notice how the compiler will run threads(Threads)Randomly quietly because if we didn't it would be hard for you to notice the difference.

To be more clear, because of the high speed of the computer in executing commands, we would have had to make the loop repeat the execution of the print command placed in each porridge thousands of times until you notice how the execution happens randomly because you will see the translator once, for example, print the word "Thread-1"700 times and once print the word "Thread-2"300 times and then come back and It prints the word "Thread-1"200 times etc.. so you would find it very difficult to notice.

We would like to point out that the numbers we mentioned are just hypotheses and not real because the order in which the print commands will be executed cannot be guaranteed, as they are executed according to the capacity of the kernel on which the thread is running.

Comprehensive examples of dealing with porridge inC++


 How to create a porridge and pass a function to it in C++

In the following example, you will learn how to create a porridge and pass a function to it.

In the following example, we have defined a function whose name func()when called that prints the statement "func is executed.."5 times in a loop with pauses for a second each time.
In the function main()we created a class object that threadexecutes the function func()in addition to creating a loop that prints the sentence "main is executed.."5 times and pauses for a second each time also until you notice how they will be executed simultaneously.

The first example is how to create a porridge and pass a function to it in C++

main.cpp
      #include <iostream>
      #include <thread>
      #include <chrono>
	  
      using namespace std;
	  
		  // five times with pauses for a second each time "func is executed.." when called it prints the text func() here we have defined a function named
      void func()
		  {
      for(int i=1; i<=5; i++)
		  {
      cout << "func is executed.. \n";
      this_thread::sleep_for(chrono::seconds(1));
		  }
		  }
	  
      int main()
		  {
		  // Going to the code immediately after it, func() to execute the thread function. Here we created an object from the class
      thread t(func);
	  
		  // five times with pauses for a second each time "main is executed.." When the compiler reaches this loop it will print the statement
      for(int i=1; i<=5; i++)
		  {
      cout << "main is executed.. \n";
		  this_thread::sleep_for(chrono::seconds(1));
		  }
	  
		  // To make the compiler wait for its commands to be executed before the main porridge stops in the program so as not to cause the t problem of the join() object here we called the function
      t.join();
	  
      return 0;
		  }
    

We will get a result similar to the following when running.

      main is executed..
      func is executed..
		  func is executed..
      main is executed..
		  func is executed..
		  main is executed..
		  func is executed..
		  main is executed..
		  func is executed..
		  main is executed..
    


In the following example, we have repeated the first example, but we have defined the function that the porridge will execute in a styleLambda Expression.

second example 

main.cpp
      #include <iostream>
      #include <thread>
      #include <chrono>
	  
      using namespace std;
	  
      int main()
		  {
		  // Lambda Expression Here we have defined and passed the function that the porridge will execute directly in it with a method
      thread t( []{
      for(int i=1; i<=5; i++)
		  {
      cout << "lambda is executed.. \n";
      this_thread::sleep_for(chrono::seconds(1));
		  }
		  });
	  
		  for(int i=1; i<=5; i++)
		  {
      cout << "main is executed.. \n";
		  this_thread::sleep_for(chrono::seconds(1));
		  }
	  
      t.join();
	  
      return 0;
		  }
    

We will get a result similar to the following when running.

      main is executed..
      lambda is executed..
		  lambda is executed..
      main is executed..
		  lambda is executed..
		  main is executed..
		  lambda is executed..
		  main is executed..
		  lambda is executed..
		  main is executed..
    


In the following example, we have repeated the first example, but we have passed a function to the porridge with a methodFunction Object.
Note: here the function that the thread will execute must contain at least one parameter, that's why we put the parameter in itn.

third example

main.cpp
      #include <iostream>
      #include <thread>
      #include <chrono>
	  
      using namespace std;
	  
		  // Here we have defined a class containing the function that we will pass to the porridge object
      class FnObject
		  {
      public:
      void operator()(int n) {
      for(int i=1; i<=n; i++)
		  {
      cout << "FnObject is executed.. \n";
      this_thread::sleep_for(chrono::seconds(1));
		  }
		  }
		  };
	  
      int main()
		  {
		  // Here we pass an object from the class we defined to the porridge object
		  // because it expects us to pass it a value of n just as we pass the value 5 to the parameter
      thread t(FnObject(), 5);
	  
      for(int i=1; i<=5; i++)
		  {
      cout << "main is executed.. \n";
      this_thread::sleep_for(chrono::seconds(1));
		  }
	  
      t.join();
	  
      return 0;
		  }
    

We will get a result similar to the following when running.

      main is executed..
      FnObject is executed..
      main is executed..
		  FnObject is executed..
		  main is executed..
		  FnObject is executed..
		  FnObject is executed..
		  main is executed..
		  FnObject is executed..
		  main is executed..
    


 How to run more than one thread at the same time in C-Plus Plus

In the following example, you will learn how to run more than one thread at a time.

In the following example, we have defined a function whose name foo(), when called, prints the sentence "foo is executed.."5 times.
Then we defined another function whose name bar()when called will print the sentence "bar is executed.."5 times as well.
Finally, we created two objects from the classthread,The first executes the function foo()and the second executes the functionbar().

The first example is a way to run more than one thread at the same time

main.cpp
      #include <iostream>
      #include <thread>
      #include <chrono>
	  
      using namespace std;
	  
		  // when called it prints the text foo() here we have defined a function named
		  // five times with pauses for a second each time "foo is executed.."
      void foo()
		  {
      for(int i=1; i<=5; i++)
		  {
      cout << "foo is executed.. \n";
      this_thread::sleep_for(chrono::seconds(1));
		  }
		  }
	  
		  // when called it prints the text bar() here we have defined a function named
		  // five times with pauses for a second each time "bar is executed.."
      void bar()
		  {
      for(int i=0; i<5; i++)
		  {
      cout << "bar is executed.. \n";
		  this_thread::sleep_for(chrono::seconds(1));
		  }
		  }
	  
      int main()
		  {
		  // bar() and the second one runs the function foo() the first runs the thread function here we created two objects from the class
      thread t1(foo);
      thread t2(bar);
	  
		  // to make the compiler wait for t2 and t1 commands to be executed from the join() objects here we called the function
		  // main() completely before it continues executing the rest of the commands in the t2 and t1 objects
      t1.join();
      t2.join();
	  
		  // when executing the next print command will be executed which means they are stopped t2 and then t1 after the object has stopped
      cout << "All threads are stopped!";
	  
      return 0;
		  }
    

We will get a result similar to the following when running.

      foo is executed..
      bar is executed..
		  bar is executed..
      foo is executed..
		  bar is executed..
		  foo is executed..
		  bar is executed..
		  foo is executed..
		  bar is executed..
		  foo is executed..
      All threads are stopped!
    


In the following example, we have repeated the first example, but we have defined the function that the porridge will execute in a styleLambda Expression.

The second example is a way to run more than one thread at the same time

main.cpp
      #include <iostream>
      #include <thread>
      #include <chrono>
	  
      using namespace std;
	  
      int main()
		  {
		  // five times with pauses for a second each time "t1 is executed.." prints the thread thread here we created an object of the class
      thread t1( []{
      for(int i=1; i<=5; i++)
		  {
      cout << "t1 is executed.. \n";
      this_thread::sleep_for(chrono::seconds(1));
		  }
		  });
	  
		  // five times with pauses for a second each time "t2 is executed.." prints the thread thread here we created an object of the class
      thread t2( []{
      for(int i=0; i<5; i++)
		  {
      cout << "t2 is executed.. \n";
		  this_thread::sleep_for(chrono::seconds(1));
		  }
		  });
	  
		  // to make the compiler wait for t2 and t1 commands to be executed from the join() objects here we called the function
		  // main() completely before it continues executing the rest of the commands in the t2 and t1 objects
      t1.join();
      t2.join();
	  
		  // when executing the next print command will be executed which means they are stopped t2 and then t1 after the object has stopped
      cout << "All threads are stopped!";
	  
      return 0;
		  }
    

We will get a result similar to the following when running.

      t1 is executed..
      t2 is executed..
		  t2 is executed..
      t1 is executed..
		  t2 is executed..
		  t1 is executed..
		  t2 is executed..
		  t1 is executed..
		  t2 is executed..
		  t1 is executed..
      All threads are stopped!
    


 How to pass values ​​to the function to be executed by the porridge in C++

In the following example, you will learn how to pass values ​​to the function that the thread will execute.

In the following example, we have defined a function whose name func()when called, we pass a text to it, and it prints it 5 times, stopping for one second each time.
In the function main()we have created two class objectsthread,The first executes the function func()and passes the text to it "Thread-1 is executed..", and the second executes the function func()and passes the text to it"Thread-2 is executed..".

Note: To pass a value to the parameter of the function executed by the thread, put a comma after the name of the function and then the value, and if you want to pass more than one value, you must put a comma between each two values.

An example showing how to pass values ​​to a function that will be executed by a thread in Cplus

main.cpp
      #include <iostream>
      #include <thread>
      #include <chrono>
	  
      using namespace std;
	  
      void func(string txt)
		  {
      for(int i=0; i<5; i++)
		  {
      cout << txt << "\n";
      this_thread::sleep_for(chrono::seconds(1));
		  }
		  }
	  
      int main()
		  {
		  // txt and each of them passes a different text in place of the parameter func() They run the thread function Here we created two class objects
      thread t1(func, "t1 is executed..");
      thread t2(func, "t2 is executed..");
	  
		  // the compiler is waiting for t2 and t1 from the two join() objects here we set we called the function
		  // Before it continues executing the rest of the commands in file t1 and t1 it must stop the objects
      t1.join();
      t2.join();
	  
      cout << "All threads are stopped!";
	  
      return 0;
		  }
    

We will get a result similar to the following when running.

      t1 is executed..
      t2 is executed..
		  t2 is executed..
      t1 is executed..
		  t2 is executed..
		  t1 is executed..
		  t2 is executed..
		  t1 is executed..
		  t2 is executed..
		  t1 is executed..
      All threads are stopped!
    

Sync in cplus plus C++

In the event that you want to run more than one thread at the same time, you should pay close attention to the operations that each thread you intend to run, because this may cause you logical problems or give you wrong results as we will explain to you in the following scenarios.


First Scenario Synchronization in C++

In the event that you build a thread whose task is to fetch the student’s marks stored in a database and then calculate his general average, and after the average was calculated and the average was displayed to the student, another thread modified some marks in the database because it found that the student had a lot of absence.
So, the result that the first porridge gave us in this case is not correct, as it was assumed that the average was calculated after the days of absence were entered into the equation that calculated his final average and exposed him if he was successful based on his final average and the number of days he attended the University.
For example, it may be one of the university's conditions that if a student is absent for 30 days during one semester, he will be considered to have failed in all subjects.

So, to solve the previous problem, it was necessary to synchronize the work of the first porridge and the second porridge.
That is, the porridge that brings the days of absence should have been run first.
Then, after storing the days of absence and the tharid stop working, the porridge must be run that fetches the student's marks and gives him the final result.


The second scenario is synchronization in C-Plus

In the event that there was a thread that wanted to modify the content of a file, and there was another thread that was reading the content of the same file.
In this case, an error will also occur, which is that the thread that is reading, will read the content of the old file, without knowing that the content of this file was updated at the time it was reading from it and performing some operations based on the content it read at the time.
To solve this problem, the work of the porridge that reads from the file and the porridge that is modified in the file had to be synchronized to ensure that they do not deal with it simultaneously.


The third scenario is synchronization in C++

If I run two porridges, at some point the two get stuck because the first porridge needs access to something the second porridge is using. At the same time, the second porridge needs to access something that the first porridge will use. This dilemma is calledDeadlock,And you can leave it as in the following picture.


To solve this problem, you can run each porridge separately or put a lock that makes the porridge that enters first to be executed completely and after the other porridge has finished executing.
To put a lock on the commands that we do not want more than one to execute simultaneously, we use a ready-made class function namedmutex.

Note: To use the class mutex, the file must be included<mutex>.



In the following example, we have created two porridges that work simultaneously, ie one after the other and not with each other.
What we did was simply create an object out of classmutex,And then calling the function lock()from it before the code that the porridge will execute, and calling the function unlock()from it after the code that the porridge will execute.

Example of synchronization in C++

main.cpp
#include <iostream>
		  #include <thread>
		  #include <chrono>
	  #include <mutex>
	  
		  using namespace std;
	  
		  // because we will use it to make the porridges we create run synchronously mutex here we have created an object of class
	  mutex mtx;
	  
		  // txt Here we have defined the function that we will pass to the porridge with the indication that we must pass a text that has the parameter location
	  void func(string txt)
		  {
		  // to make any other thread that wants to execute the code wait for mtx from the lock() object here we called the function
	  mtx.lock();
	  
	  cout << "Starting " << txt << "\n";
	  
		  // Here we create a loop that prints the name of the thread that is currently executing 3 times, stopping for a second each time
	  for(int i=0; i<5; i++)
		  {
	  cout << txt << "\n";
		  this_thread::sleep_for(chrono::seconds(1));
		  }
	  
	  cout << "Ending " << txt << "\n";
	  
		  // in order to make any other thread waiting to be able to start executing the commands of the mtx function from the unlock() object here we called the function
	  mtx.unlock();
		  }
	  
		  int main()
		  {
		  // txt and each of them passes a different text in place of the parameter func() They run the thread function Here we created two class objects
	  thread t1(func, "Thread-1");
	  thread t2(func, "Thread-2");
	  
		  // the compiler is waiting for t2 and t1 from the two join() objects here we set we called the function
		  // Before it continues executing the rest of the commands in file t1 and t1 it must stop the objects
	  t1.join();
	  t2.join();
	  
	  cout << "All threads are end!";
	  
	  return 0;
		  }
	

We will get the following result when running.

Starting Thread-1         <-- Here the execution of the first thread has begun
Thread-1
Thread-1
Thread-1
Ending Thread-1           <-- Here the execution of the first thread commands has ended
Starting Thread-2         < -- Here the execution of the second thread has started
Thread-2
Thread-2
Thread-2
Ending Thread-2           <--
Both threads are end !