这两位博主写的很详细
https://www.jianshu.com/u/9456fecb5f96
https://blog.csdn.net/QIANGWEIYUAN/article/details/88792621
一 概念:
(1).进程是一个应用程序被操作系统拉起来加载到内存之后从开始到结束这样的一个过程. (进程是程序的一次执行
)进程通常由程序、数据和进程控制块组成。
进程的两个基本属性:
1.可拥有资源的独立单位:进程可以获取操作系统分配的资源,如内存等
2.可独立调度和分配的基本单位:进程可以参与操作系统的调度,参与CPU的竞争,得到分配的时间片
获得处理机(CPU)运行。
进程在创建、撤销和切换中,系统必须为之付出较大的时空开销,因此系统进程开启数量不易过多
。比如同时开启很多个线程电脑会 卡死。
(2)线程,线程是进程中的一个实体,是被系统独立分配和调度的基本单位。也就是说线程是CPU可执行
的和调度的最小单位。
二:thread 线程常用的函数及案例:
(1)常用函数: join()、 jionable() 、swap()、detach()、 get_id()
join():主要用来阻塞主线程,等待其他子线程执行完毕,正常退出,避免主线程
先于其他子线程执行完毕,退出从而导致整个程序异常。
detach() :可以让主线程正常退出,无需等待其他程序执行完成,子程序被挪到后台运行
这些子程序会被c++运行库接管,当这些子程序运行完毕后,由运行库清理线程相关资源
detach 会使线程失去控制,但是如果希望子线程做的事情与我们控制与否不影响,那么可以使用
detach(),同时 detach()不能与join()同时使用,否则会发生异常;
get_id()获取线程 id号
(2)线程间通信(生产者消费者模型案例)
mutex() 互斥锁 std::unique_lock<std::mutex> lock(mtx1);
条件变量必须得使用同一个
#include<iostream>
#include<thread>
#include<windows.h>
#include <mutex>
#include <condition_variable>
#include <vector>
using namespace std;
bool single = true;
std::mutex mtx1;
std::mutex mtx2;
std::condition_variable cv;
std::vector<int> vec;
void RainProducer()
{
int i = 0;
do
{
std::unique_lock<std::mutex> lock(mtx1);
while (!vec.empty())
{
cv.wait(lock);
}
vec.push_back(i);
cout << "one_生产者 " << i++ << endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
cv.notify_all();
if (i == 10)
break;
} while (single);
}
消费者线程函数
void RainConsumer()
{
int j = 0;
do
{
//mtx2.lock();
//获取mtx 互斥锁资源
std::unique_lock<std::mutex> lock(mtx2);
while (vec.empty())
{
cv.wait(lock);
}
int data = vec.back();
vec.pop_back();
cv.notify_all();
cout << "two_消费产品 " << data*2 << endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
j++;
if (j == 10)
break;
} while (single );
}
void test11()
{
std::lock_guard<std::mutex> lock(mtx2);
for (int i = 0; i < 10; i++)
{
Sleep(200);
}
}
消费者线程函数
void test22()
{
std::lock_guard<std::mutex> lock(mtx1);
for (int j = 0; j < 10; j++)
{
Sleep(100);
}
}
int main()
{
clock_t s1,s2;
s1 = clock();
thread t1(RainProducer);
thread t2(RainConsumer);
t1.join();
t2.join();
s2 = clock();
cout << "total consume time is " << s2-s1 << endl;
/*cout << t1.get_id()<< endl;
cout << t2.get_id() << endl;*/
system("pause");
return 0;
}
多线程之间通信,生产者消费者模型案例 使用要注意std::mutex的使用,若生产者和消费者使用同一个互斥锁,则程序按照交替进行的穿行程序,使用两个mutex的话,可以是程序并行起来,边生产边消费,极大的减少程序运行时间。
如在生产者函数中 void RainProducer() 中的 std::unique_lock<std::mutex> lock(mtx1);
消费者函数中 void RainConsumer() 中的 std::unique_lock<std::mutex> lock(mtx1);
#include<iostream>
#include<thread>
#include<windows.h>
#include <mutex>
#include <condition_variable>
#include <vector>
using namespace std;
//bool single = true;
std::mutex mtx1;
std::mutex mtx2;
std::condition_variable consume, produce,pro;
std::vector<int> vec;
std::vector<int> vecStart;
std::vector<int> vecStartTmp;
std::vector<int> vecConsume;
bool single = false;
bool ss = true;
int num = 10;
void test1()
{
std::unique_lock<std::mutex> lock(mtx1);
while (!vecStartTmp.empty())
{
consume.wait(lock);
}
for (int i = 0; i < num; i++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
vecStart.push_back(i);
}
vecStartTmp.swap(vecStart);
cout << "one_生产者...................... " << vecStartTmp.size() << endl;
consume.notify_all();
lock.unlock();
}
void test2()
{
std::unique_lock<std::mutex> lock(mtx1);
while (vecStartTmp.empty())
{
consume.wait(lock);
}
vec.swap(vecStartTmp);
vecStartTmp.clear();
consume.notify_all();
lock.unlock();
for (int i = 0; i < vec.size(); i++)
{
std::unique_lock<std::mutex> lock2(mtx2);
while (!vecConsume.empty())
{
pro.wait(lock2);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
cout << "two_消费者 " << vec[i] << endl;
vecConsume.push_back(vec[i]);
pro.notify_all();
lock2.unlock();
}
}
void test3()
{
for (int i = 0; i < num; i++)
{
std::unique_lock<std::mutex> lock2(mtx2);
while (vecConsume.empty())
{
pro.wait(lock2);
}
int data = vecConsume.back();
vecConsume.pop_back();;
pro.notify_all();
lock2.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
cout << "three_消费者" << data * 2 << endl;
}
}
void start()
{
for (int j = 0; j < num; j++)
{
test1();
}
}
int main()
{
int n = 0;
clock_t s1, s2,s3,s4;
s1 = clock();
thread t1(start);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));//等待thread 1 线程先执行
do
{
if (n == 10)
break;
thread t2(test2);
thread t3(test3);
t2.join();
t3.join();
n++;
} while (ss);
t1.join();
s2 = clock();
cout << "total consume time is " << s2 - s1 << endl;
//char name[20];
//cin.getline(name, 20);
//cout << sizeof(name) / sizeof(name[0]) << endl;
cout << *name << endl;
//for (int i = 0; i < sizeof(name) / sizeof(name[0]); i++)
//{
// cout << *(name + i) << endl;
//}
/*cout << t1.get_id()<< endl;
cout << t2.get_id() << endl;*/
system("pause");
return 0;
}
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
std::mutex mtx;
std::condition_variable cv1, cv2;
class FooBar {
private:
volatile int count = 0;
int n;
public:
//FooBar() {};
FooBar() {
this->n = 10;
}
void foo() {
for (int i = 0; i < n; i++) {
std::unique_lock<std::mutex>lock(mtx);
while (count != 0)
{
cv1.wait(lock);
count = 0;
}
// printFoo() outputs "foo". Do not change or remove this line.
cout << "first function start " << endl;
count=1;
cv1.notify_one();
lock.unlock();
}
}
void bar() {
for (int i = 0; i < n; i++) {
std::unique_lock<std::mutex>lock(mtx);
while (count != 1)
{
cv1.wait(lock);
count = 1;
}
// printBar() outputs "bar". Do not change or remove this line.
cout << "second function start " << endl;
count=0;
cv1.notify_one();
lock.unlock();
}
}
};
int main()
{
FooBar f;
thread t1(&FooBar::bar, f);
thread t2(&FooBar::foo, f);
t1.join();
t2.join();
system("pause");
return 0;
}
注意:
pthread_join()
- 调用pthread_join()会使得当前线程阻塞等待
- 当目标线程退出函数会立即返回,然后线程资源回收
- 目标函数必须是joinable的(线程创建后默认就是joinable)
- 如果retval不是空指针,那么会在retval返回目标线程退出状态
- 如果当前线程在等待过程被杀死,那么目标线程还是继续joinable
- 请勿多个线程同时join一个目标线程
pthread_detach()
- pthread_detach将一条线程由joinable转换成detached
- 分离线程的好处是线程退出后,省略了join的步骤,自己回收资源
- 已经处于detached的线程切勿再次detached
什么时候分离线程会存在陷阱?
- 一个分离线程使用一个共享资源
- 分离线程的生命周期比所使用的资源的生命周期长
那么就有可能造成未知的错误。
例如:
子线程使用一个全局对象(object),子线程与程序一并退出。
由于程序退出的顺序是:
- 对象析构
- 子线程退出
- 主线程退出
因此,程序退出过程中,全局对象会调用自己的析构函数,此时,对象生命周期结束,对象被销毁。但是,子线程还没马上死亡,子线程仍然有可能继续调用已经析构的对象。期望的结果是安全退出程序,结果却造成了未知的错误。
方法论
- 不要使用分离线程
- 分离线程不要使用共享资源
- 主线程退出前主动销毁子线程(主线程是主动的)
- 使用条件变量,主线程等待子线程退出(主线程是被动的)
- 使用
void quick_exit (int status)
函数,直接退出,不调用析构函数
既使用quick_exit(),又需要析构
使用 int at_quick_exit( void (*func)(void) )
函数,注册退出函数。
PS
quick_exit()
系列函数包含在<stdlib.h>
中,但Windows不一定(具体和编译器、编译器版本都有关)。