概念:
多线程是实现并发或并行的一种手段,并行是指两个或多个独立的操作同时进行,并发是在一个时间段内执行多个操作。
在单核时代,多个线程是并发的,在一个时间段内轮流执行;而在多核时代,多个线程可以实现真正的并行,在多核上真正独立的并行执行。例如现在常见的4核4线程可以并行4个线程;4核8线程则使用了超线程技术,把一个物理核模拟为2个逻辑核心,可以并行8个线程。
线程调用:
每个进程至少有一个线程,执行main()函数的线程, 其余线程有其各自的入口函数(线程函数)。
启用线程有以下规则:
(1)当线程执行完线程函数后, 线程也会退出;
(2)如果不传入线程函数, 线程不会运行.
(3)线程函数不能重载, 否则不能编译.
(4)在为一个线程创建了一个 thread 对象后, 如果线程已启动, 必须要明确是加入线程(join)还是分离线程(detach).
(5)如果 thread 对象销毁之前还没有调用 join 或 detach, 程序就会发生异常而终止,这是因为thread 的析构函数会调用 terminate()。因此, 在销毁之前必须确保线程调用了join或detach,即便是有异常发生时。
(6)join()是简单粗暴的等待线程完成, 即阻塞主线程(main). [注]:如果在线程启动后到主线程在调用 join() 前的代码中发生了异常, 此时将会导致主线程永远没有机会执行.
(7) detach()将子线程从主线程中分离,独立运行,不会阻塞主线程;
(8) 其他函数,
thread::hardware_concurrency() - 返回 CPU 核心线程数. 如果无法查询系统信息时, 返回0.
get_id() - 返回 thread 对象关联的线程的 id. 如果所有权已转移, 或线程函数已返回, 返回0.
this_thread::get_id() 取得当前线程的 id.
编程实例:
1. join()
#include <iostream>
#include <thread>
#include <Windows.h>
using namespace std;
void thread01()
{
for (int i = 0; i < 5; i++)
{
cout << "Thread 01 is working !" << endl;
Sleep(100);
}
}
void thread02()
{
for (int i = 0; i < 5; i++)
{
cout << "Thread 02 is working !" << endl;
Sleep(200);
}
}
int main()
{
///1. join阻塞主线程
thread task01(thread01);
thread task02(thread02);
task01.join();
task02.join();
for (int i = 0; i < 5; i++)
{
cout << "Main thread is working !" << endl;
Sleep(200);
}
system("pause");
return 0;
}
2. detach主线程和两个子线程并行执行
#include <iostream>
#include <thread>
#include <Windows.h>
using namespace std;
void thread01()
{
for (int i = 0; i < 5; i++)
{
cout << "Thread 01 is working !" << endl;
Sleep(100);
}
}
void thread02()
{
for (int i = 0; i < 5; i++)
{
cout << "Thread 02 is working !" << endl;
Sleep(200);
}
}
int main()
{
///detach不阻塞主线程
thread task01(thread01);
thread task02(thread02);
task01.detach();
task02.detach();
for (int i = 0; i < 5; i++)
{
cout << "Main thread is working !" << endl;
Sleep(200);
}
system("pause");
return 0;
}
3. 带参数子线程
#include <iostream>
#include <thread>
#include <Windows.h>
using namespace std;
//定义带参数子线程
void thread01(int num)
{
for (int i = 0; i < num; i++)
{
cout << "Thread 01 is working !" << endl;
Sleep(100);
}
}
void thread02(int num)
{
for (int i = 0; i < num; i++)
{
cout << "Thread 02 is working !" << endl;
Sleep(200);
}
}
int main()
{
thread task01(thread01, 5); //带参数子线程
thread task02(thread02, 5);
task01.detach();
task02.detach();
for (int i = 0; i < 5; i++)
{
cout << "Main thread is working !" << endl;
Sleep(200);
}
system("pause");
}
4. 多线程数据竞争
多个线程同时对同一变量进行操作的时候,如果不对变量做一些保护处理,有可能导致处理结果异常:
#include <iostream>
#include <thread>
#include <Windows.h>
using namespace std;
int totalNum = 100;
void thread01()
{
while (totalNum > 0)
{
cout << totalNum << endl;
totalNum--;
Sleep(100);
}
}
void thread02()
{
while (totalNum > 0)
{
cout << totalNum << endl;
totalNum--;
Sleep(100);
}
}
int main()
{
thread task01(thread01);
thread task02(thread02);
task01.detach();
task02.detach();
system("pause");
}
异常:一是有很多变量被重复输出了,而有的变量没有被输出;二是正常情况下每个线程输出的数据后应该紧跟一个换行符,但这里大部分却是另一个线程的输出。
这是由于第一个线程对变量操作的过程中,第二个线程也对同一个变量进行各操作,导致第一个线程处理完后的输出有可能是线程二操作的结果。
针对这种数据竞争的情况,可以使用线程互斥对象mutex保持数据同步。
mutex类的使用需要包含头文件mutex。
#include <iostream>
#include <thread>
#include <Windows.h>
#include <mutex>
using namespace std;
mutex mu; //线程互斥对象
int totalNum = 100;
void thread01()
{
while (totalNum > 0)
{
mu.lock(); //同步数据锁
cout << totalNum << endl;
totalNum--;
Sleep(100);
mu.unlock(); //解除锁定
}
}
void thread02()
{
while (totalNum > 0)
{
mu.lock();
cout << totalNum << endl;
totalNum--;
Sleep(100);
mu.unlock();
}
}
int main()
{
thread task01(thread01);
thread task02(thread02);
task01.detach();
task02.detach();
system("pause");
}
加入互斥后正常输出。
5. 常见示例:
#include <thread>
#include <iostream>
using namespace std;
int parallel_fun(int idx)
{
cout << idx << endl;
return 0;
}
int main()
{
int flages[4];
thread th1([&]() {flages[0] = parallel_fun(1); });
thread th2([&]() {flages[1] = parallel_fun(2); });
thread th3([&]() {flages[2] = parallel_fun(3); });
thread th4([&]() {flages[3] = parallel_fun(4); });
th1.join();
th2.join();
th3.join();
th4.join();
return 0;
}
结果:
参考文献:
1. https://www.cnblogs.com/wangguchangqing/p/6134635.html
2. https://www.cnblogs.com/diysoul/p/5934622.html
3. https://blog.csdn.net/dcrmg/article/details/53912941