C++11 Getting started with multithreaded programming basics

Source: Internet
Author: User
Tags joins mutex thread class

1. Create a new thread in c++11

In each C + + application, there is a default main thread, the main function, in c++11, where we can create other threads by creating objects of the Std::thread class, each std:: Thread object can be associated with a line threads, just include the header file < Thread>. You can use the std:: Thread object to attach a callback that will be executed when the new thread starts. These callbacks can be function pointers, function objects, lambda functions.
Thread objects can be created by Std::thread Thobj (< callback>), and new threads will start immediately after the new object is created, and the passed callbacks will be executed in parallel with the thread that was started. In addition, any thread can wait for another thread to exit by calling the Join () function on the thread's object.
To create a thread using a function pointer:

Main.cpp#include <iostream> #include <thread>void thread_function () {for        (int i = 0; i < 5; i++)            Std::cout << "thread function excuting" << Std::endl;} int main () {        std::thread threadobj (thread_function);        for (int i = 0; i < 5, i++)            std::cout << "Display from Mainthread" << Std::endl;   Threadobj.join ();       Std::cout << "Exit of Main function" << Std::endl;    return 0;}

To create a thread using a function object:

#include <iostream> #include <thread>class displaythread {    public:void operator () () {for                (int i = 0; I < 100; i++)                    std::cout << "Display Thread excecuting" << Std::endl;    }; int main () {        std::thread threadobj ((Displaythread ()));        for (int i = 0; i <, i++)            std::cout << "Display from Main Thread" << Std::endl;        Std::cout << "Waiting for Thread to complete" << Std::endl;    Threadobj.join ();        Std::cout << "Exiting from Main Thread" << Std::endl;        return 0;}

CmakeLists.txt

Cmake_minimum_required (VERSION 3.10) project (thread_test) set (Cmake_cxx_standard) Find_package (Threads required) Add_executable (thread_test main.cpp) target_link_libraries (Thread_test ${cmake_thread_libs_init})

Each Std::thread object has an associated id,std::thread::get_id ()--The ID of the corresponding thread object given in the member function;
STD::THIS_THREAD::GET_ID ()-gives the ID of the current thread, and if the Std::thread object does not have an associated thread, get_id () returns the Std::thread::id object of the default construct: "Not any thread", Std::thread::id can also represent an ID.

2.joining and Detaching Threads

Once the thread is started, another thread can wait for the thread to execute by calling the join () function on the call to the Std::thread object:

Std::thread threadobj (FUNCPTR); Threadobj.join ();

For example, the main thread starts 10 threads, and after the boot is finished, the main function waits for them to finish, and after the join all the threads, the main function continues to execute:

 #include <iostream> #include <thread> #include <algorithm>class workerthread{public:void operator () () {std::cout<< "Worker Thread" <<std::this_thread::get_i    D () << "is Excecuting" <<std::endl;    }};            int main () {std::vector<std::thread> threadlist;    for (int i = 0; i < i++) {Threadlist.push_back (Std::thread (Workerthread ()));    }//Now Wait is the worker thread to finish i.e. Call Join () function on each of the Std::thread object std::cout<< ' Wait for all ' the worker thread to finish ' &L        t;<std::endl;        Std::for_each (Threadlist.begin (), Threadlist.end (), STD::MEM_FN (&std::thread::join));        std::cout<< "Exiting from Main Thread" <<std::endl; return 0;} 

Detach can detach a thread from a thread object, make the thread execute as a background thread, and the current thread will not block. However, after detach, the thread cannot be contacted. If the thread execution function uses a temporary variable that may cause problems, the thread calls the detach to run in the background, The temporary variable may have been destroyed, so the line routines accesses the variable that has been destroyed and needs to call STD in the Std::thread object::d Etach () function:

Std::thread threadobj (funcptr) Threadobj.detach ();

After calling detach (), the Std::thread object is no longer associated with the actual execution line threads, call Detach () and join () on the thread handle with caution.

3. Passing parameters to the thread

To pass parameters to a thread's associative object or function, simply pass the argument to the Std::thread constructor, and by default all parameters will be copied to the internal storage of the new thread.
To pass parameters to a thread:

#include <iostream> #include <string> #include <thread>void threadcallback (int x, std::string str) {      std::cout << "Passed number =" << x << Std::endl;      Std::cout << "Passed String =" << str << Std::endl;} int main () {      int x = ten;      std::string str = "Sample string";      Std::thread threadobj (threadcallback, X, str);  Threadobj.join ();    return 0;}

To pass a reference to a thread:

#include <iostream> #include <thread>void threadcallback (int const& x) {      int& y = const_cast< Int&> (x);  y++;    Std::cout << "Inside Thread x =" << x << Std::endl;} int main () {      int x = 9;      Std::cout << "in Main thread:before Thread Start x =" << x << Std::endl;      Std::thread threadobj (Threadcallback, x);  Threadobj.join ();    Std::cout << "in Main thread:after Thread Joins x =" << x << Std::endl;    return 0;}

The output is:
In Main thread:before Thread Start x = 9
Inside Thread x = 10
In Main thread:after Thread Joins x = 9

Process finished with exit code 0
Even if Threadcallback accepts the argument as a reference, but does not change the value of x in Main, it is not visible outside the thread reference. This is because the x in the thread function Threadcallback is a temporary value that references the copy in the stack of the new thread, which can be modified using std::ref:

#include <iostream> #include <thread>void threadcallback (int const& x) {      int& y = const_cast< Int&> (x);  y++;    Std::cout << "Inside Thread x =" << x << Std::endl;} int main () {      int x = 9;  Std::cout << "in Main thread:before Thread Start x =" << x << Std::endl;      Std::thread threadobj (Threadcallback, Std::ref (x));  Threadobj.join ();    Std::cout << "in Main thread:after Thread Joins x =" << x << Std::endl;    return 0;}

The output is:
In Main thread:before Thread Start x = 9
Inside Thread x = 10
In Main thread:after Thread Joins x = 10

Process finished with exit code 0
Specify a pointer to a member function of a class as a thread function, pass the pointer to the member function as a callback function, and point the pointer to the object as the second parameter:

#include <iostream> #include <thread>class dummyclass {public:  Dummyclass () {}  Dummyclass (const dummyclass& obj) {}    void samplememberfunction (int x) {          std::cout << "Inside samplememberfunction" < ;< x << Std::endl;  }};    int main () {      dummyclass dummyobj;        int x = ten;        Std::thread threadobj (&dummyclass::samplememberfunction, &dummyobj, x);      Threadobj.join ();        return 0;}

4. Sharing of data between threads and competition conditions

Data sharing between multiple threads is simple, but this kind of data sharing in the program can cause problems, one of which is the race condition. When two or more threads perform a set of operations in parallel, accessing the same memory location, at which point one or more of them modifies the data in the memory location, which can cause some unexpected results, which is the race condition. Competitive conditions are often more difficult to discover and reproduce because they do not always occur, only if the relative order of two or more threads to perform the action causes unexpected results.
For example, create 5 threads that share an object of class wallet and add 100 of dollars in parallel using the Addmoney () member function. So, if the money in the original purse is 0, then the money in the wallet should be 500 after all the threads have finished competing, but in some cases the money in the wallet may be much less than 500, as all threads simultaneously modify the shared data.
The test is as follows:

 #include <iostream> #include <thread> #include <algorithm>class Wallet {i    NT Mmoney;        Public:wallet (): Mmoney (0) {} int Getmoney () {return mmoney;}        void Addmoney (int money) {for (int i = 0; i < money; i++) {mmoney++;        }}};int Testmultithreadwallet () {Wallet walletobject;        Std::vector<std::thread> threads;    for (int i = 0; i < 5; i++) {Threads.push_back (Std::thread (&wallet::addmoney, &walletobject, 100));    } for (int i = 0; i < 5; i++) {threads.at (i). Join (); } return Walletobject.getmoney ();}            int main () {int val = 0; for (int k = 0; k < k++) {if ((Val=testmultithreadwallet ())! =) {std::        cout << "Error at count =" << k << "money in Wallet =" << val << Std::endl; }} return 0;} 

Each thread adds the same member variable "Mmoney" in parallel, seemingly a line, but this "nmoney++" is actually converted to 3 machine commands:
• Load "Mmoney" variable in register
• Increase the value of register
• Update the "Mmoney" variable with the value of register
In this case, an increment is ignored, because instead of increasing the Mmoney variable, instead of adding a different register, the value of the "Mmoney" variable is overwritten.

5. Using mutexes to deal with competitive conditions

In order to deal with the competition conditions in multi-threaded environment, we need mutex mutex, before modifying or reading the shared data, we need to lock the data and unlock the data after the modification is complete. In the C++11 thread Library, mutexes in the < Mutexe > header file, the class that represents the mutex is Std::mutex.
Dealing with the above problem, the Wallet class provides a way to add money to the wallet, and uses the same wallet object in different threads, so we need to lock the Addmoney () method of the wallet. Add a lock before adding money to the wallet, and unlock before leaving the function, see Code: Wallet Class internally maintain money, and provide function Addmoney (), this member function first acquires a lock, then adds the specified amount to the Wallet object's money, Finally release the lock.

#include <iostream> #include <thread> #include <vector> #include <mutex>class Wallet {int            Mmoney;        Std::mutex Mutex;public:wallet (): Mmoney (0) {} int Getmoney () {return mmoney;}                void Addmoney (int money) {mutex.lock ();        for (int i = 0; i < money; i++) {mmoney++;    } mutex.unlock ();        }};int Testmultithreadwallet () {Wallet walletobject;        Std::vector<std::thread> threads;    for (int i = 0; i < 5; ++i) {Threads.push_back (Std::thread (&wallet::addmoney, &walletobject, 1000));    } for (int i = 0; i < threads.size (); i++) {threads.at (i). Join (); } return Walletobject.getmoney ();}            int main () {int val = 0; for (int k = 0; k < k++) {if (val = Testmultithreadwallet ())! =) {std:: cout << "Error at count=" << K << "Money in wallet" &Lt;< Val << Std::endl; }} return 0;}

This ensures that the money in the wallet does not appear to be less than 5000, because the mutex in Addmoney () ensures that another thread can modify it only after one thread has modified it, but what if we forget to release the lock after the function is finished? In this case, one thread will exit without releasing the lock, and the other threads will remain waiting, in order to avoid this, we should use Std::lock_guard, which is a template class that implements Rall for the mutex, which wraps the mutex within its object, and locks the attached mutex in its constructor, and when its destructor is called, it releases the mutex.

Class Wallet {      int mmoney;      Std::mutex Mutex; Public:  Wallet (): Mmoney (0) {}  int Getmoney () {return mmoney;}    void Addmoney (int money) {      std::lock_guard<std::mutex> lockguard (mutex);      for (int i = 0; i < Mmoney; ++i) {        //If an exception occurs at this point, Lockguadr's destructor will be called mmoney++ because of the stack expansion      ;            Once the function exits, the destructor of the Lockguard object is called, and the mutex is freed in the destructor}}  ;

6. Condition variables

A

Condition variable is an event used to make a signaling between 2 threads, a thread can wait for it to get a signal, and other threads can signal it. In c++11, the condition variable requires a header file < Condition_variable>, and the condition variable also requires a mutex lock. How the
condition variable is run:
• Thread 1 calls the wait condition variable, obtains the mutex mutex internally and checks whether the condition is satisfied;
• If not, release the lock and wait for the condition variable to get signaled (the thread is blocked). The wait () function of the condition variable provides both operations atomically;
• Another thread, such as thread 2, signals a condition variable when the condition is met;
• Once thread 1 is waiting for its recovered condition variable to signal, thread 1 acquires the mutex. And check whether the condition associated with the condition variable is met, or whether it is a superior call, and if multiple threads are waiting, then Notify_one will only unlock one thread;
• If it is a superior call, it calls the wait () function again. The primary member function of the
condition variable:
Wait ()
It causes the current thread to block until the condition variable gets signaled or a false wake occurs;
It atomically releases the attached mutex, blocks the current thread, and adds it to the list of threads waiting on the current condition variable object, and when a thread calls Notify_one () or Notify_all () on the same condition variable, the thread is unblocked;
This behavior can also be false, so after unblocking, you need to check the condition again;
A callback function passes to the function, calls it to check whether it is a false call, or does satisfy the real condition;
When the thread is unblocked, the wait () function acquires the mutex lock. and checks whether the condition is satisfied, if the condition is not met, atomically releases the additional mutex, blocks the current thread, and adds it to the list of threads waiting on the current condition variable object.
notify_one ()
If all threads are waiting for the same condition variable object, Notify_one will unblock one of the waiting threads.
Notify_all ()
If all threads are waiting for the same condition variable object, then Notify_all will unblock all waiting threads.

#include <iostream> #include <thread> #include <functional> #include <mutex> #include <        Condition_variable>using namespace std::p laceholders;class application {Std::mutex M_mutex;        Std::condition_variable M_condvar;    BOOL M_bdataloaded;public:application () {m_bdataloaded = false; } void LoadData () {//causes the thread to sleep 1 seconds std::this_thread::sleep_for (Std::chrono::milliseconds (                1000));        Std::cout << "Loading Data from XML" << Std::endl;        Lock data std::lock_guard<std::mutex> Guard (M_mutex);        Flag set to TRUE indicates that the data is loaded m_bdataloaded = true;    Notification condition variable m_condvar.notify_one ();    } bool Isdataloaded () {return m_bdataloaded;        } void Maintask () {std::cout << "do some handshaking" << Std::endl;        Get lock std::unique_lock<std::mutex> Mlock (M_mutex);        Start waiting for the condition variable to get the signalWait () releases the lock internally and causes the thread to block//Once the condition variable is signaled, resumes the thread and acquires the lock again//and then detects whether the condition is satisfied, continues if the condition is met, or enters the wait m_condvar.wait again (mlo                CK, Std::bind (&application::isdataloaded, this));    Std::cout << "Do processing on loaded Data" << Std::endl;        }};int Main () {application app;        Std::thread thread_1 (&application::maintask, &app);    Std::thread thread_2 (&application::loaddata, &app);    Thread_2.join ();    Thread_1.join (); return 0;}
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.