多线程概念
并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。
同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。
并行(Parallel):当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。其实决定并行的因素不是CPU的数量,而是CPU的核心数量,比如一个CPU多个核也可以并行。
进程:正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源,比如说CPU(寄存器),IO,内存,网络资源等。同样一个程序,同一时刻被两次运行了,那么他们就是两个独立的进程。
线程(Thread):是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
进程与线程的区别:
- 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;进程是系统资源分配的单位,线程是系统调度的单位。
- 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
- 进程之间相互独立,进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自己的栈和栈指针,程序计数器等寄存器。
- 调度和切换:线程上下文切换比进程上下文切换要快得多。
多线程的好处
响应性:为了更好的利用cpu的资源,如果只有一个线程,则第二个任务必须等到第一个任务结束后才能进行,如果使用多线程则在主线程执行任务的同时可以执行其他任务,而不需要等待;
资源共享:进程之间不能共享数据,线程可以;
经济性:系统创建进程需要为该进程重新分配系统资源,创建线程代价比较小;
可伸缩性:在多处理器架构中,多线程可以在多个处理器上并行运行,增加了并行性。
单核CPU使用多线程技术可以提升性能,但效果有限。在单核CPU上,多线程可以提高程序的响应性和效率,特别是在一个线程等待(如I/O操作)时,其他线程可以继续执行,从而更有效地利用CPU时间。然而,由于单核CPU在任何给定时间只能执行一个线程,所以多线程并不能提高计算任务的实际并行性。多线程在单核CPU上主要是通过时间分片来模拟并发执行,这可以提高用户体验,但不会像在多核CPU上那样显著提高计算性能。而多核CPU有多个处理核心,这意味着多核CPU可以同时处理多个任务。
join和detach函数
#include<iostream>
#include<thread>
void WorkFunction(int index)
{
int n = 10;
for (int i=0; i<n; ++i)
{
std::cout << "index:" << index << ",other thread," << i << std::endl;
}
}
int main()
{
std::thread th[3];
for (int i = 0; i < 3; ++i)
{
th[i] = std::thread(WorkFunction, i); //创建线程
//th[i].join();
}
for (int i = 0; i < 3; ++i)
{
th[i].detach();
//th[i].detach();
}
int n = 5;
for (int i = 0; i<n; ++i)
{
std::cout << "main thread" << std::endl;
}
return 0;
}
join
: 调用join
会让主线程等待,直到新线程执行完毕。这是一种同步操作,确保新线程可以安全地完成其任务,而主线程会在新线程结束后继续执行detach
: 调用detach
会将新线程与主线程分离,允许新线程独立于主线程运行。一旦分离,新线程的执行将独立于创建它的线程,当新线程结束时,它的资源会被自动释放
如果不调用 join
或 detach
,当主线程结束时,新线程可能还在运行,这会导致程序尝试销毁还在运行的线程,从而引发错误。
互斥锁和自解锁
#include<iostream>
#include<thread>
#include<mutex>
std::mutex m;
void WorkFunction(int index)
{
int n = 10;
for (int i=0; i<n; ++i)
{
m.lock();
std::cout << "index:" << index << ",other thread," << i << std::endl;
m.unlock();
}
}
int sum = 0;
void WorkSumFunction(int index)
{
for (int i = 0; i<10000000; ++i)
{
std::lock_guard<std::mutex> lg(m); //自解锁是封装mutex的对象,在创建时调用m.lock,析构时调用m.unlock,避免忘记解锁
//m.lock();
sum++;
//m.unlock();
}
}
int main()
{
std::thread th[3];
for (int i = 0; i < 3; ++i)
{
th[i] = std::thread(WorkFunction, i); //创建线程
//th[i].join();
}
for (int i = 0; i < 3; ++i)
{
th[i].join();
//th[i].detach();
}
int n = 5;
for (int i = 0; i<n; ++i)
{
std::cout << "main thread" << std::endl;
}
return 0;
}
原子操作
#include<atomic>
atomic_int sum; //对基本类型进行原子操作,不用加锁