与 C++11 多线程相关的头文件地方
C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是
该头文主要声明了两个类, std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。
该头文件主要声明了 std::thread 类,另外 std::this_thread 命名空间也在该头文件中。
该头文件主要声明了与互斥量(mutex)相关的类,包括 std::mutex 系列类,std::lock_guard, std::unique_lock, 以及其他的类型和函数。
该头文件主要声明了与条件变量相关的类,包括 std::condition_variable 和 std::condition_variable_any。
该头文件主要声明了 std::promise, std::package_task 两个 Provider 类,以及 std::future 和 std::shared_future 两个 Future 类,另外还有一些与之相关的类型和函数,std::async() 函数就声明在此头文件中。
#include
#include
using namespace std;
void th_function()
{
std::cout << "hello thread." << std::endl;
}
int main(int argc, char *argv[])
{
std::thread t(th_function);
t.join();
return 0;
}
(1). 默认构造函数,创建一个空的 thread 执行对象。
(2). 初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
(3). 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
(4). move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。
注意:可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached.
看一个例子:
这个例子中创建了两个线程,其中一个线程函数带参数,大家仔细分析上面的thread类的构函数,看如何创建线程。
// thread example
#include // std::cout
#include // std::thread
void thr_function1()
{
for (int i = 0; i != 10; ++i)
{
std::cout << "thread 1 print " << i << std::endl;
}
}
void thr_function2(int n)
{
std::cout << "thread 1 print " << n << std::endl;
}
int main()
{
std::thread t1(thr_function1); // spawn new thread that calls foo()
std::thread t2(thr_function2, 111); // spawn new thread that calls bar(0)
std::cout << "main, foo and bar now execute concurrently...\n";
// synchronize threads:
t1.join(); // pauses until first finishes
t2.join(); // pauses until second finishes
std::cout << "thread 1 and htread 2 completed.\n";
return 0;
}
大家的输出可能不是这样,有可能是下面这样的,不要紧,因为这个例子中没使用多线程的异步机制,线程之间存在竞争,看稍后文章讲解所以输出可能是下面这样的情况。
三、move 赋值操作
move (1)
thread& operator= (thread&& rhs) noexcept;
copy [deleted] (2)
thread& operator= (const thread&) = delete;
(1). move 赋值操作,如果当前对象不可 joinable,需要传递一个右值引用(rhs)给 move 赋值操作;如果当前对象可被 joinable,则 terminate() 报错。
(2). 拷贝赋值操作被禁用,thread 对象不可被拷贝。
看下面的例子。
// example for thread::operator=
#include // std::cout
#include // std::thread, std::this_thread::sleep_for
#include // std::chrono::seconds
void pause_thread(int n)
{
std::this_thread::sleep_for(std::chrono::seconds(n));//c++11 this_thread 类
std::cout << "pause of " << n << " seconds ended\n";
}
int main()
{
std::thread threads[5]; // default-constructed threads
std::cout << "Spawning 5 threads...\n";
for (int i = 0; i<5; ++i)
threads[i] = std::thread(pause_thread, i + 1); // move-assign threads 这里调用move复制函数
std::cout << "Done spawning threads. Now waiting for them to join:\n";
for (int i = 0; i<5; ++i)
threads[i].join();
std::cout << "All threads joined!\n";
return 0;
}
1、成员类型和成员函数。
2、std::thread 构造函数。
3、异步。
4、多线程传递参数。
5、join、detach。
6、获取CPU核心个数。
7、CPP原子变量与线程安全。
8、lambda与多线程。
9、时间等待相关问题。
10、线程功能拓展。
11、多线程可变参数。
12、线程交换。
13、线程移动。
(1).默认构造函数,创建一个空的 thread 执行对象。
(2).初始化构造函数,创建一个 thread 对象,该 thread 对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
(3).拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
(4).move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。
注意:可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached。
#include<thread>
#include<chrono>
using namespace std;
void fun1(int n) //初始化构造函数
{
cout << "Thread " << n << " executing\n";
n += 10;
this_thread::sleep_for(chrono::milliseconds(10));
}
void fun2(int & n) //拷贝构造函数
{
cout << "Thread " << n << " executing\n";
n += 20;
this_thread::sleep_for(chrono::milliseconds(10));
}
int main()
{
int n = 0;
thread t1; //t1不是一个thread
thread t2(fun1, n + 1); //按照值传递
t2.join();
cout << "n=" << n << '\n';
n = 10;
thread t3(fun2, ref(n)); //引用
thread t4(move(t3)); //t4执行t3,t3不是thread
t4.join();
cout << "n=" << n << '\n';
return 0;
}
运行结果:
Thread 1 executing
n=0
Thread 10 executing
n=30
#include
using namespace std;
void show()
{
cout << "hello cplusplus!" << endl;
}
int main()
{
//栈上
thread t1(show); //根据函数初始化执行
thread t2(show);
thread t3(show);
//线程数组
thread th[3]{thread(show), thread(show), thread(show)};
//堆上
thread *pt1(new thread(show));
thread *pt2(new thread(show));
thread *pt3(new thread(show));
//线程指针数组
thread *pth(new thread[3]{thread(show), thread(show), thread(show)});
return 0;
}
#include
using namespace std;
void show(const char *str, const int id)
{
cout << "线程 " << id + 1 << " :" << str << endl;
}
int main()
{
thread t1(show, "hello cplusplus!", 0);
thread t2(show, "你好,C++!", 1);
thread t3(show, "hello!", 2);
return 0;
}
运行结果:
线程 1线程 2 :你好,C++!线程 3 :hello!
:hello cplusplus!
#include
#include
using namespace std;
void show()
{
cout << "hello cplusplus!" << endl;
}
int main()
{
array threads = { thread(show), thread(show), thread(show) };
for (int i = 0; i < 3; i++)
{
cout << threads[i].joinable() << endl;//判断线程是否可以join
threads[i].join();//主线程等待当前线程执行完成再退出
}
return 0;
}
运行结果:
hello cplusplus!
hello cplusplus!
1
hello cplusplus!
1
1
总结:
join 是让当前主线程等待所有的子线程执行完,才能退出。
detach例子如下:
#include
using namespace std;
void show()
{
cout << "hello cplusplus!" << endl;
}
int main()
{
thread th(show);
//th.join();
th.detach();//脱离主线程的绑定,主线程挂了,子线程不报错,子线程执行完自动退出。
//detach以后,子线程会成为孤儿线程,线程之间将无法通信。
cout << th.joinable() << endl;
return 0;
}
运行结果:
hello cplusplus!
结论:
线程 detach 脱离主线程的绑定,主线程挂了,子线程不报错,子线程执行完自动退出。
线程 detach以后,子线程会成为孤儿线程,线程之间将无法通信。
例如:
#include
using namespace std;
int main()
{
auto n = thread::hardware_concurrency();//获取cpu核心个数
cout << n << endl;
return 0;
}
运行结果:
8
结论:
通过 thread::hardware_concurrency() 获取 CPU 核心的个数。
问题例如:
**#include
using namespace std;
const int N = 100000000;
int num = 0;
void run()
{
for (int i = 0; i < N; i++)
{
num++;
}
}
int main()
{
clock_t start = clock();
thread t1(run);
thread t2(run);
t1.join();
t2.join();
clock_t end = clock();
cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
return 0;
}
运行结果:
num=143653419,用时 730 ms
从上述代码执行的结果,发现结果并不是我们预计的200000000,这是由于线程之间发生冲突,从而导致结果不正确。
为了解决此问题,有以下方法:
(1)互斥量。
例如:
#include
#include
using namespace std;
const int N = 100000000;
int num(0);
mutex m;
void run()
{
for (int i = 0; i < N; i++)
{
m.lock();
num++;
m.unlock();
}
}
int main()
{
clock_t start = clock();
thread t1(run);
thread t2(run);
t1.join();
t2.join();
clock_t end = clock();
cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
return 0;
}
运行结果:
num=200000000,用时 128323 ms
不难发现,通过互斥量后运算结果正确,但是计算速度很慢,原因主要是互斥量加解锁需要时间。
互斥量详细内容 请参考下篇文章C++11 并发之std::mutex。
(2)原子变量。
例如:
#include
#include
using namespace std;
const int N = 100000000;
atomic_int num{ 0 };//不会发生线程冲突,线程安全
void run()
{
for (int i = 0; i < N; i++)
{
num++;
}
}
int main()
{
clock_t start = clock();
thread t1(run);
thread t2(run);
t1.join();
t2.join();
clock_t end = clock();
cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
return 0;
}
运行结果:
num=200000000,用时 29732 ms
不难发现,通过原子变量后运算结果正确,计算速度一般。
原子变量详细内容 请参考C++11 并发之std::atomic。
(3)加入 join 。
例如:
#include
using namespace std;
const int N = 100000000;
int num = 0;
void run()
{
for (int i = 0; i < N; i++)
{
num++;
}
}
int main()
{
clock_t start = clock();
thread t1(run);
t1.join();
thread t2(run);
t2.join();
clock_t end = clock();
cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
return 0;
}
运行结果:
num=200000000,用时 626 ms
不难发现,通过原子变量后运算结果正确,计算速度也很理想。
8、lambda与多线程。
例如:
#include
using namespace std;
int main()
{
auto fun = [](const char *str) {cout << str << endl; };
thread t1(fun, "hello world!");
thread t2(fun, "hello beijing!");
return 0;
}
运行结果:
hello world!
hello beijing
9、时间等待相关问题。
例如:
#include
#include
using namespace std;
int main()
{
thread th1([]()
{
//让线程等待3秒
this_thread::sleep_for(chrono::seconds(3));
//让cpu执行其他空闲的线程
this_thread::yield();
//线程id
cout << this_thread::get_id() << endl;
});
return 0;
}
例如:
#include
using namespace std;
class MyThread :public thread //继承thread
{
public:
//子类MyThread()继承thread()的构造函数
MyThread() : thread()
{
}
//MyThread()初始化构造函数
template<typename T, typename...Args>
MyThread(T&&func, Args&&...args) : thread(forward(func), forward(args)...)
{
}
void showcmd(const char *str) //运行system
{
system(str);
}
};
int main()
{
MyThread th1([]()
{
cout << "hello" << endl;
});
th1.showcmd("calc"); //运行calc
//lambda
MyThread th2([](const char * str)
{
cout << "hello" << str << endl;
}, " this is MyThread");
th2.showcmd("notepad");//运行notepad
return 0;
}
运行结果:
hello
//运行calc
hello this is MyThread
//运行notepad
例如:
#include
#include
using namespace std;
int show(const char *fun, ...)
{
va_list ap;//指针
va_start(ap, fun);//开始
vprintf(fun, ap);//调用
va_end(ap);
return 0;
}
int main()
{
thread t1(show, "%s %d %c %f", "hello world!", 100, 'A', 3.14159);
return 0;
}
运行结果:
hello world! 100 A 3.14159
12、线程交换。
例如:
#include
using namespace std;
int main()
{
thread t1([]()
{
cout << "thread1" << endl;
});
thread t2([]()
{
cout << "thread2" << endl;
});
cout << "thread1' id is " << t1.get_id() << endl;
cout << "thread2' id is " << t2.get_id() << endl;
cout << "swap after:" << endl;
swap(t1, t2);//线程交换
cout << "thread1' id is " << t1.get_id() << endl;
cout << "thread2' id is " << t2.get_id() << endl;
return 0;
}
运行结果:
thread1
thread2
thread1' id is 4836
thread2' id is 4724
swap after:
thread1' id is 4724
thread2' id is 4836
两个线程通过 swap 进行交换。
例如:
#include
using namespace std;
int main()
{
thread t1([]()
{
cout << "thread1" << endl;
});
cout << "thread1' id is " << t1.get_id() << endl;
thread t2 = move(t1);;
cout << "thread2' id is " << t2.get_id() << endl;
return 0;
}
运行结果:
thread1
thread1' id is 5620
thread2' id is 5620
从上述代码中,线程t2可以通过 move 移动 t1 来获取 t1 的全部属性,而 t1 却销毁了。