C++多线程标准库学习

创建线程的三种不同方法

需包含头文件

#include <thread>

使用函数指针创建线程

#include <iostream>
#include <thread>
 
void thread_function()
{
	for (int i = 0; i < 1000000000; i++);
	std::cout << "thread function Executing" << std::endl;
}

int main()
{
	std::thread threadObj(thread_function);
	std::cout << "Display From MainThread" << std::endl;
	threadObj.join(); // 等待线程结束
	std::cout << "Exit of Main function" << std::endl;
	return 0;
}

使用函数对象创建线程

#include <iostream>
#include <thread>

class DisplayThread
{
public:
    void operator()()
    {
        for (int i = 0; i < 100000; i++)
            std::cout << "Display Thread Executing" << std::endl;
    }
};

int main()
{
    std::thread threadObj((DisplayThread()));
    for (int i = 0; i < 100000; 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;
}

使用 Lambda 函数创建线程

#include <iostream>
#include <thread>

int main()
{
    std::thread threadObj([] {
        for (int i = 0; i < 10000; i++)
            std::cout << "Display Thread Executing" << std::endl;
        });

    for (int i = 0; i < 10000; i++)
        std::cout << "Display From Main Thread" << std::endl;

    threadObj.join();
    std::cout << "Exiting from Main Thread" << std::endl;
    return 0;
}

区分线程

#include <iostream>
#include <thread>

void thread_function()
{
    std::cout << "Inside Thread :: ID  = " << std::this_thread::get_id() << std::endl;
}

int main()
{
    std::thread threadObj1(thread_function);
    std::thread threadObj2(thread_function);

    if (threadObj1.get_id() != threadObj2.get_id())
        std::cout << "Both Threads have different IDs" << std::endl;

    for (int i = 0; i < 1000000000; i++);

    std::cout << "From Main Thread :: ID of Thread 1 = " << threadObj1.get_id() << std::endl;
    std::cout << "From Main Thread :: ID of Thread 2 = " << threadObj2.get_id() << std::endl;

    threadObj1.join(); // id需要在join之前获取,否则会变成0
    threadObj2.join();
    return 0;
}

join和detach线程

std::thread::join()

#include <iostream>
#include <thread>
#include <vector> // vector
#include <algorithm> // for_each
#include <functional> // mem_fn


class WorkerThread
{
public:
	void operator()()
	{
		std::cout << "Worker Thread " << std::this_thread::get_id() << " is Executing" << std::endl;
	}
};

int main()
{
	std::vector<std::thread> threadList;
	for (int i = 0; i < 10; i++)
	{
		threadList.push_back(std::thread(WorkerThread()));
	}
	// Now wait for all 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" << 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;
}

std::thread::detach()

#include <iostream>
#include <thread>

void my_fun()
{
	for (int i = 0; i < 10000; i++);
	std::cout << "my function end." << std::endl;
}

int main()
{
	std::thread my_thread(my_fun);
	std::cout << "detaching" << std::endl;
	my_thread.detach(); // 线程独立运行,两边数据不互通
	std::cout << my_thread.get_id() << "is detached" << std::endl; // 无法获取id
	std::cout << "main end." << std::endl;
	return 0;
}

小心在线程句柄上调用 detach() 和 join()

  • join或者detach之前先要检查joinable,只能二选一,只能用一次
  • 主进程结束之前要二选一

使用RAII

#include <iostream>
#include <thread>

class ThreadRAII
{
	std::thread& m_thread;
public:
	ThreadRAII(std::thread& threadObj) : m_thread(threadObj)
	{

	}
	~ThreadRAII()
	{
		// Check if thread is joinable then detach the thread
		if (m_thread.joinable())
		{
			m_thread.join();
			// m_thread.detach();
		}
	}
};
void thread_function()
{
	for (int i = 0; i < 10000; i++);
	std::cout << "thread_function Executing" << std::endl;
}

int main()
{
	std::thread threadObj(thread_function);
	// for (int i = 0; i < 1000000; i++);

	// If we comment this Line, then program will crash
	ThreadRAII wrapperObj(threadObj);
	return 0;
}

小心地将参数传递给线程

在C++11中将简单参数传递给std::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 = 10;
    std::string str = "Sample String";
    std::thread threadObj(threadCallback, x, str);
    threadObj.join();
    return 0;
}

如何错误地将参数传递给C++11中的线程

p指向的数据有可能被销毁

#include <iostream>
#include <thread>

void newThreadCallback(int* p)
{
    std::cout << "Inside Thread :  "" : p = " << *p << std::endl;
    *p = 19;
    std::chrono::milliseconds dura(1000);
    std::this_thread::sleep_for(dura);
    std::cout << "Inside Thread :  "" : p = " << *p << std::endl;
}

void startNewThread()
{
    int i = 10;
    std::cout << "Inside Main Thread :  "" : i = " << i << std::endl;
    std::thread t(newThreadCallback, &i);
    t.detach();
    std::cout << "Inside Main Thread :  "" : i = " << i << std::endl;
}

int main()
{
    startNewThread();
    std::chrono::milliseconds dura(2000);
    std::this_thread::sleep_for(dura);
    return 0;
}

调用变量时有可能已经delete

#include <iostream>
#include <thread>

void newThreadCallback(int* p)
{
    std::cout << "Inside Thread :  "" : *p = " << *p << std::endl;
    std::chrono::milliseconds dura(1000);
    std::this_thread::sleep_for(dura);
    *p = 19;
}

void startNewThread()
{
    int* p = new int();
    *p = 10;
    std::cout << "Inside Main Thread :  "" : *p = " << *p << std::endl;
    std::thread t(newThreadCallback, p);
    t.detach();
    delete p;
    p = NULL;
}

int main()
{
    startNewThread();
    std::chrono::milliseconds dura(2000);
    std::this_thread::sleep_for(dura);
    return 0;
}

如何在C++11中传递对std::thread的引用

错误示例,修改仅限于线程内

#include <iostream>
#include <thread>

void threadCallback(int const& x) // 此处一定要加const,否则报错,估计是先传值,再把值传引用
{
    int& y = const_cast<int&>(x);
    y++;
    std::cout << "Inside Thread x = " << x << std::endl; // 10
}

int main()
{
    int x = 9;
    std::cout << "In Main Thread : Before Thread Start x = " << x << std::endl; // 9
    std::thread threadObj(threadCallback, x);
    threadObj.join();
    std::cout << "In Main Thread : After Thread Joins x = " << x << std::endl; // 9
    return 0;
}

正确操作

#include <iostream>
#include <thread>

void threadCallback(int & x)
{
    x++;
    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;
}

https://www.cnblogs.com/chen-cs/p/13056703.html

将指向类的成员函数的指针分配为线程函数:

#include <iostream>
#include <thread>

class DummyClass {
public:
    DummyClass():y(0)
    {}
    DummyClass(const DummyClass& obj):y(obj.y)
    {}
    void sampleMemberFunction(int x)
    {
        std::cout << "Inside sampleMemberFunction " << x << std::endl;
        std::cout << "Inside sampleMemberFunction " << y << std::endl;
        y++;
    }
    int y;
};

int main() {

    DummyClass dummyObj;
    int x = 10;
    dummyObj.y = 5;
    std::thread threadObj1(&DummyClass::sampleMemberFunction, &dummyObj, x);
    std::thread threadObj2(&DummyClass::sampleMemberFunction, &dummyObj, x);
    threadObj1.join();
    threadObj2.join();
    return 0;
}

数据共享和竞争条件

线程1线程2
将“mMoney”加载到寄存器
将“mMoney”加载到寄存器
将寄存器的值加一
将寄存器的值加一
将寄存器的值存到“mMoney”中
将寄存器的值存到“mMoney”中
会发生竞争的一种情况
#include <iostream>
#include <vector>
#include <thread>

// race condition
class Wallet
{
    int mMoney;
public:
    Wallet() :mMoney(0) {}
    int getMoney() { return mMoney; }
    void addMoney(int money)
    {
        for (int i = 0; i < money; ++i)
        {
            mMoney++;
        }
    }
};

int testMultithreadedWallet()
{
    Wallet walletObject;
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++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;
    int k = 0;
    while(true)
    {
        if ((val = testMultithreadedWallet()) != 10000)
        {
            std::cout << "Error at count = " << k << " Money in Wallet = " << val << std::endl;
            break;
        }
        k++;
    }
    return 0;
}

使用互斥锁处理竞争条件

需包含的头文件

#include<mutex>

一般用法

#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(); // 如果mutex处于lock状态,lock()指令就会等待mutex.unlock()
        for (int i = 0; i < money; ++i)
        {
            mMoney++;
        }
        mutex.unlock();
    }
};

lock_guard(RAII)

#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)
    {
        std::lock_guard<std::mutex> lockGuard(mutex);
        // In constructor it locks the mutex
        for (int i = 0; i < money; ++i)
        {
            // If some exception occurs at this
            // poin then destructor of lockGuard
            // will be called due to stack unwinding.
            //
            mMoney++;
        }
        // Once function exits, then destructor
        // of lockGuard Object will be called.
        // In destructor it unlocks the mutex.
    }
};

公共部分

int testMultithreadedWallet()
{
    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 < 1000; k++)
    {
        if ((val = testMultithreadedWallet()) != 5000)
        {
            std::cout << "Error at count = " << k << "  Money in Wallet = " << val << std::endl;
            //break;
        }
    }
    return 0;
}

事件处理

示例场景

使用互斥锁

#include<iostream>
#include<thread>
#include<mutex>

// boolean check loop
class Application
{
	std::mutex m_mutex;
	bool m_bDataLoaded;
public:
	Application()
	{
		m_bDataLoaded = false;
	}

	void loadData()
	{
		// Make This Thread sleep for 1 Second
		std::cout << "Loading Data from XML" << std::endl;
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
		// Lock The Data structure
		std::lock_guard<std::mutex> guard(m_mutex);
		// Set the flag to true, means data is loaded
		m_bDataLoaded = true;
	}

	void mainTask()
	{
		std::cout << "Do Some Handshaking" << std::endl;
		// Acquire the Lock
		m_mutex.lock();
		// Check if flag is set to true or not
		while (m_bDataLoaded != true)
		{
			// Release the lock
			m_mutex.unlock();
			//sleep for 100 milli seconds
			std::this_thread::sleep_for(std::chrono::milliseconds(100));
			// Acquire the lock
			m_mutex.lock();
		}
		// Release the lock
		m_mutex.unlock();
		//Doc processing on loaded Data
		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;
}

条件变量

需包含的头文件

#include <condition_variable>

例子同上,但是条件变量

#include <iostream>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>

using namespace std::placeholders;

class Application
{
    std::mutex m_mutex;
    std::condition_variable m_condVar;
    bool m_bDataLoaded;
public:
    Application()
    {
        m_bDataLoaded = false;
    }

    void loadData()
    {
        // Make This Thread sleep for 1 Second
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        std::cout << "Loading Data from XML" << std::endl;
        // Lock The Data structure
        std::lock_guard<std::mutex> guard(m_mutex);
        // Set the flag to true, means data is loaded
        m_bDataLoaded = true;
        // Notify the condition variable
        m_condVar.notify_one();
    }

    bool isDataLoaded()
    {
        return m_bDataLoaded;
    }

    void mainTask()
    {
        std::cout << "Do Some Handshaking" << std::endl;
        // Acquire the lock
        std::unique_lock<std::mutex> mlock(m_mutex);
        // Start waiting for the Condition Variable to get signaled
        // Wait() will internally release the lock and make the thread to block
        // As soon as condition variable get signaled, resume the thread and
        // again acquire the lock. Then check if condition is met or not
        // If condition is met then continue else again go in wait.
        m_condVar.wait(mlock, 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;
}

std::future,std::promise和从线程返回值

需包含的头文件

#include <future>
工作流程
#include <iostream>
#include <thread>
#include <future>

void initiazer(std::promise<int>* promObj)
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Inside Thread" << std::endl;
    promObj->set_value(35);
}

int main()
{
    std::promise<int> promiseObj;
    std::future<int> futureObj = promiseObj.get_future();
    std::thread th(initiazer, &promiseObj);
    std::cout << futureObj.get() << std::endl;
    th.join();
    return 0;
}

参考链接

C++11 Multithreading – Part 1 : Three Different ways to Create Threads

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据