C++多线程编程

2023-05-16

c++ 11 之后有了标准的线程库:

C++ 11发布之前,C++并没有对多线程编程的专门支持,C++ 11通过标准库引入了对多线程的支持,大大方便了程序员的工作,本篇我们对这部分进行简要的介绍。需要说明的是,C++ 11标准库内部包裹了pthread库,因此,编译程序的时候需要加上-lpthread连接选项.

#include <thread>
#include <condition_variable>
#include <mutex>

 

1 std::thread

关键点

a. C++ 11中创建线程非常简单,使用std::thread类就可以,thread类定义于thread头文件,构造thread对象时传入一个可调用对象作为参数(如果可调用对象有参数,把参数同时传入),这样构造完成后,新的线程马上被创建,同时执行该可调用对象;

b. 用std::thread默认的构造函数构造的对象不关联任何线程;判断一个thread对象是否关联某个线程,使用joinable()接口,如果返回true,表明该对象关联着某个线程(即使该线程已经执行结束);

c. "joinable"的对象析构前,必须调用join()接口等待线程结束,或者调用detach()接口解除与线程的关联,否则会抛异常;

d. 正在执行的线程从关联的对象detach后会自主执行直至结束,对应的对象变成不关联任何线程的对象,joinable()将返回false;

 

e. std::thread没有拷贝构造函数和拷贝赋值操作符,因此不支持复制操作(但是可以move),也就是说,没有两个 std::thread对象会表示同一执行线程;

f. 容易知道,如下几种情况下,std::thread对象是不关联任何线程的(对这种对象调用join或detach接口会抛异常):

默认构造的thread对象;

被移动后的thread对象;

detach 或 join 后的thread对象;

 

这个程序创建了两个线程,分别对变量num进行了10000次++操作,由于两个线程同时运行,++num也没有加锁保护,所以最后的输出结果在10000到20000之间,有一定随机性,也证明了++num不是原子操作;

 

2 std::mutex   (轻松实现互斥)

常做多线程编程的人一定对mutex(互斥)非常熟悉,C++ 11当然也支持mutex,通过mutex可以方便的对临界区域加锁,std::mutex类定义于mutex头文件,是用于保护共享数据避免从多个线程同时访问的同步原语。它提供了lock,try_lock,unlock等几个接口,功能如下:

调用方线程从成功调用lock()或try_lock()开始,到unlock()为止占有mutex对象;

线程占有mutex时,所有其他线程若试图要求mutex的所有权,则将阻塞(对于 lock 的调用)或收到false返回值(对于 try_lock );

调用方线程在调用 lock 或 try_lock 前必须不占有mutex。

我们用mutex改写上面的例子,达到两个线程不会同时++num的目的,改写如下:

 

经过mutex对++语句的保护,使同一时刻,只可能有一个线程对num变量进行++操作,因此,这段程序的输出必然是20000。

mutex和thread一样,不可复制(拷贝构造函数和拷贝赋值操作符都被删除),而且,mutex也不可移动;

备注:

 

a.操作系统提供mutex可以设置属性,C++11根据mutext的属性提供四种的互斥量,分别是

  • std::mutex,最常用,普遍的互斥量(默认属性), 
  • std::recursive_mutex ,允许同一线程使用recursive_mutext多次加锁,然后使用相同次数的解锁操作解锁。mutex多次加锁会造成死锁
  • std::timed_mutex,在mutex上增加了时间的属性。增加了两个成员函数try_lock_for(),try_lock_until(),分别接收一个时间范围,再给定的时间内如果互斥量被锁主了,线程阻塞,超过时间,返回false。
  • std::recursive_timed_mutex,增加递归和时间属性

 

b.mutex成员函数加锁解锁

  • lock(),互斥量加锁,如果互斥量已被加锁,线程阻塞
  • bool try_lock(),尝试加锁,如果互斥量未被加锁,则执行加锁操作,返回true;如果互斥量已被加锁,返回false,线程不阻塞。
  • void unlock(),解锁互斥量

c. mutex RAII式的加锁解锁

  • std::lock_guard,管理mutex的类。对象构建时传入mutex,会自动对mutex加入,直到离开类的作用域,析构时完成解锁。RAII式的栈对象能保证在异常情形下mutex可以在lock_guard对象析构被解锁。
  • std::unique_lock 与 lock_guard功能类似,但是比lock_guard的功能更强大。比如std::unique_lock维护了互斥量的状态,可通过bool owns_lock()访问,当locked时返回true,否则返回false

 

3 std::lock_guard   (有作用域的mutex ,让 程序更稳定,防止死锁)

很容易想到,mutex的lock和unlock必须成对调用,lock之后忘记调用unlock将是非常严重的错误,再次lock时会造成死锁。有时候一段程序中会有各种出口,如return,continue,break等等语句,在每个出口前记得unlock已经加锁的mutex是有一定负担的,而假如程序段中有抛异常的情况,就更为隐蔽棘手,C++ 11提供了更好的解决方案,对的,RAII,本系列文章多次提到RAII,想必大家应该不陌生。

类模板std::lock_guard是mutex封装器,通过便利的RAII机制在其作用域内占有mutex。

创建lock_guard对象时,它试图接收给定mutex的所有权。当程序流程离开创建lock_guard对象的作用域时,lock_guard对象被自动销毁并释放mutex,lock_guard类也是不可复制的。

一般,需要加锁的代码段,我们用{}括起来形成一个作用域,括号的开端创建lock_guard对象,把mutex对象作为参数传入lock_guard的构造函数即可,比如上面的例子加锁的部分,我们可以改写如下:

 

 

 

进入作用域,临时对象guard创建,获取mutex控制权(构造函数里调用了mutex的lock接口),离开作用域,临时对象guard销毁,释放了mutex(析构函数里调用了unlock接口),这是对mutex的更为安全的操作方式(对异常导致的执行路径改变也有效),大家在实践中应该多多使用;

C++11 std::unique_lock与std::lock_guard区别及多线程应用实例

 

C++多线程编程中通常会对共享的数据进行写保护,以防止多线程在对共享数据成员进行读写时造成资源争抢导致程序出现未定义的行为。通常的做法是在修改共享数据成员的时候进行加锁--mutex。在使用锁的时候通常是在对共享数据进行修改之前进行lock操作,在写完之后再进行unlock操作,进场会出现由于疏忽导致由于lock之后在离开共享成员操作区域时忘记unlock,导致死锁。

针对以上的问题,C++11中引入了std::unique_lock与std::lock_guard两种数据结构。通过对lock和unlock进行一次薄的封装,实现自动unlock的功能。

 

std::mutex mut;  
  
void insert_data()  
{  
       std::lock_guard<std::mutex> lk(mut);  
       queue.push_back(data);  
}  
  
void process_data()  
{  
       std::unqiue_lock<std::mutex> lk(mut);  
       queue.pop();  
}  

std::unique_lock 与std::lock_guard都能实现自动加锁与解锁功能,但是std::unique_lock要比std::lock_guard更灵活,但是更灵活的代价是占用空间相对更大一点且相对更慢一点。

 

std::unique_lock 的构造函数的数目相对来说比 std::lock_guard 多,其中一方面也是因为 std::unique_lock 更加灵活,从而在构造 std::unique_lock 对象时可以接受额外的参数。总地来说,std::unique_lock 构造函数如下:

default (1)
unique_lock() noexcept;
locking (2)
explicit unique_lock(mutex_type& m);
try-locking (3)
unique_lock(mutex_type& m, try_to_lock_t tag);
deferred (4)
unique_lock(mutex_type& m, defer_lock_t tag) noexcept;
adopting (5)
unique_lock(mutex_type& m, adopt_lock_t tag);
locking for (6)
template <class Rep, class Period>
unique_lock(mutex_type& m, const chrono::duration<Rep,Period>& rel_time);
locking until (7)
template <class Clock, class Duration>
unique_lock(mutex_type& m, const chrono::time_point<Clock,Duration>& abs_time);
copy [deleted] (8)
unique_lock(const unique_lock&) = delete;
move (9)
unique_lock(unique_lock&& x);

下面我们来分别介绍以上各个构造函数:

(1) 默认构造函数

新创建的 unique_lock 对象不管理任何 Mutex 对象。

(2) locking 初始化

新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.lock() 对 Mutex 对象进行上锁,如果此时另外某个 unique_lock 对象已经管理了该 Mutex 对象 m,则当前线程将会被阻塞。

(3) try-locking 初始化

新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.try_lock() 对 Mutex 对象进行上锁,但如果上锁不成功,并不会阻塞当前线程。

(4) deferred 初始化

新创建的 unique_lock 对象管理 Mutex 对象 m,但是在初始化的时候并不锁住 Mutex 对象。 m 应该是一个没有当前线程锁住的 Mutex 对象。

(5) adopting 初始化

新创建的 unique_lock 对象管理 Mutex 对象 m, m 应该是一个已经被当前线程锁住的 Mutex 对象。(并且当前新创建的 unique_lock 对象拥有对锁(Lock)的所有权)。

(6) locking 一段时间(duration)

新创建的 unique_lock 对象管理 Mutex 对象 m,并试图通过调用 m.try_lock_for(rel_time) 来锁住 Mutex 对象一段时间(rel_time)。

(7) locking 直到某个时间点(time point)

新创建的 unique_lock 对象管理 Mutex 对象m,并试图通过调用 m.try_lock_until(abs_time) 来在某个时间点(abs_time)之前锁住 Mutex 对象。

(8) 拷贝构造 [被禁用]

unique_lock 对象不能被拷贝构造。

(9) 移动(move)构造

新创建的 unique_lock 对象获得了由 x 所管理的 Mutex 对象的所有权(包括当前 Mutex 的状态)。调用 move 构造之后, x 对象如同通过默认构造函数所创建的,就不再管理任何 Mutex 对象了。

综上所述,由 (2) 和 (5) 创建的 unique_lock 对象通常拥有 Mutex 对象的锁。而通过 (1) 和 (4) 创建的则不会拥有锁。通过 (3),(6) 和 (7) 创建的 unique_lock 对象,则在 lock 成功时获得锁。

 

 

 

 

 

线程同步

std::mutex mtx_syn;
std::condition_variable cv_syn;
std::condition_variable cv_syn_1;
bool ready = false;
void threadA(int id) {
	while (1)
	{
		std::unique_lock<std::mutex> lck(mtx_syn);
		while (!ready) cv_syn.wait(lck);
		// ...
		std::cout << "thread " << id << '\n';
		Sleep(500);
		cv_syn.notify_all();   //cpu 轮询执行 所有被唤醒的线程。
		cv_syn.wait(lck);
	}
	
}
void threadB(int id) {
	while (1)
	{
//新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.lock() 对 Mutex 对象进行上锁,如果此时另外某个 unique_lock 对象已经管理了该 Mutex 对象 m,则当前线程将会被阻塞
		std::unique_lock<std::mutex> lck(mtx_syn);
		while (!ready) cv_syn.wait(lck);
		// ...
		std::cout << "thread " << id << '\n';
		Sleep(500);
		cv_syn.notify_all();
		cv_syn.wait(lck);
	}
}


void threadC(int id) {
	while (1)
	{
		std::unique_lock<std::mutex> lck(mtx_syn);
		while (!ready) cv_syn_1.wait(lck);
		// ...
		std::cout << "thread " << id << '\n';
		Sleep(500);
		cv_syn_1.notify_all();
		cv_syn_1.wait(lck);
	}
}


void go()
{
	std::unique_lock<std::mutex> lck(mtx_syn);
	ready = true;
	cv_syn.notify_one();
}
//线程同步
	std::thread threads[5];
	// spawn 10 threads:
	//for (int i = 0; i<5; ++i)
	//	threads[i] = std::thread(print_id, i);
	threads[0] = std::thread(threadA, 0);
	threads[1] = std::thread(threadB, 1);
	threads[2] = std::thread(threadC, 2);  //该线程 与 0, 1 无关,不影响 0,1 线程的同步,因为用的不是一个 condition_variable
	std::cout << "2 threads ready to race...\n";
	go();                       // go!

	for (auto& th : threads) th.join();

 

thread 使用

 

#include <iostream>

#include <thread>

std::thread::id main_thread_id = std::this_thread::get_id();

void hello()  
{
    std::cout << "Hello Concurrent World\n";
    if (main_thread_id == std::this_thread::get_id())
        std::cout << "This is the main thread.\n";
    else
        std::cout << "This is not the main thread.\n";
}

void pause_thread(int n) {
    std::this_thread::sleep_for(std::chrono::seconds(n));
    std::cout << "pause of " << n << " seconds ended\n";
}

int main() {
    std::thread t(hello);
    std::cout << t.hardware_concurrency() << std::endl;//可以并发执行多少个(不准确)
    std::cout << "native_handle " << t.native_handle() << std::endl;//可以并发执行多少个(不准确)
    t.join();
    std::thread a(hello);
    a.detach();
    std::thread threads[5];                         // 默认构造线程

    std::cout << "Spawning 5 threads...\n";
    for (int i = 0; i < 5; ++i)
        threads[i] = std::thread(pause_thread, i + 1);   // move-assign threads
    std::cout << "Done spawning threads. Now waiting for them to join:\n";
    for (auto &thread : threads)
        thread.join();
    std::cout << "All threads joined!\n";
}

 

多线程使用实例:

#include <iostream>
#include <opencv2/opencv.hpp>
#include "../data300w/util/Util.h"
#include "cunpd.hpp"
#include <ctime>
#include <io.h>
#include <direct.h>  
#include <thread>
#include <condition_variable>
#include <mutex>
#include <Windows.h>


using namespace std;
using namespace cv;
using namespace glasssix;


#define ZOOM_ 1.0
#define SIZE 96


extern void  splitString(const string& s, vector<string>& v, const string& c);


template <class Type>
Type stringToNum(const string& str)
{
	istringstream iss(str);
	Type num;
	iss >> num;
	return num;
}
void writeHistoFile(std::string filePath, vector<int> & densi_data)
{
	if (filePath == "" || densi_data.size() == 0)
	{
		return;
	}
	ofstream in;
	in.open(filePath, ios::app);   //ios::trunc
	int length = densi_data.size();
	for (int i = 0; i < length; i++)
	{
		string dataline = to_string(densi_data[i]);
		in << dataline << "\n";
	}
	in.close();
}


float getHorizontal(LandMark &landmark)
{
	float tan_theta = (landmark.points[1].y - landmark.points[0].y) / (landmark.points[1].x - landmark.points[0].x);
	float theta = atan(tan_theta);
	return theta * 180 / 3.1415926;
}
void showHistogram(vector<float> & hor_data)
{
	int densi[60] = { 0 };
	int length = hor_data.size();
	for (int i = 0; i < length; i++)
	{
		if (floor((hor_data[i] + 30)) >= 0 && floor((hor_data[i] + 30)) < 60)
		{
			densi[(int)floor((hor_data[i] + 30))]++;
		}


		if (floor((hor_data[i] + 30)) < 0)
		{
			densi[0]++;
		}
		else if (floor((hor_data[i] + 30)) >= 60)
		{
			densi[60]++;
		}
	}
	string density_text = "D:\\UMD\\density_text.txt";
	vector<int>density_data(densi, densi + 60);
	writeHistoFile(density_text, density_data);


	Mat histImg;
	histImg.create(1000, 1600, CV_8UC3);
	histImg.setTo(0);
	int offset = 10;
	for (int i = 0; i < 60; i++)
	{
		double tmpCount = densi[i];
		rectangle(histImg, Point2f(offset + i * 25, 1000), Point2f(offset + i * 25, 1000 - tmpCount / 15.0), Scalar::all(255), -1);  //画出直方图  
		putText(histImg, to_string(i - 29), Point2f(offset + i * 25 + 3, 1000 - 3), 0.3, 0.3, Scalar(0, 0, 255));
		Point2f pt0;
		pt0.x = offset + i * 25;
		pt0.y = 1000 - densi[i] / 15.0;


		Point2f pt1;
		pt1.x = offset + (i + 1) * 25;
		pt1.y = 1000 - densi[i + 1] / 15.0;
		line(histImg, pt0, pt1, Scalar(255, 0, 0), 1); //连接直方图的顶点  
	}
	imshow("hist", histImg);
	waitKey(0);
}
void getDatahor(string file1, vector<float> & hor_data)
{
	int mark_num = 5;
	DataPrepareUtil dpu;
	vector<LandMark> data;
	dpu.readFileData(file1, data, mark_num);
	int length = data.size();
	for (int i = 0; i < length; i++)
	{
		float hor = getHorizontal(data[i]);
		hor_data.emplace_back(hor);
	}
}


void rotation(float theta, Mat &img, Mat &dst, Size img_size, LandMark &landmark, int mark_num)
{
	//rotation
	Mat mat = img;
	Point2f center(img_size.width / 2, img_size.height / 2);
	double angle = theta;


	Mat rot = getRotationMatrix2D(center, angle, 1);
	Rect bbox = RotatedRect(center, mat.size(), angle).boundingRect();


	cv::warpAffine(mat, dst, rot, bbox.size());


	for (int j = 0; j < mark_num; j++)
	{
		float theta = -3.1415926 / (180 / angle);
		float x1 = landmark.points[j].x - rot.at<double>(1, 2);
		float y1 = landmark.points[j].y - rot.at<double>(0, 2);
		landmark.points[j].x = x1 * cos(theta) - y1 * sin(theta);
		landmark.points[j].y = x1 * sin(theta) + y1 * cos(theta);


		//circle(dst, Point(x, y), 2, Scalar(255, 0, 0));
	}
	//cv::imshow("dst", dst);
	//cv::waitKey(0);
}


void augment_data(string img_path, string img_text, string result_path, string result_text)
{
	DataPrepareUtil dpu;
	int mark_num = 5;
	srand((unsigned)time(NULL));


	vector<LandMark> data;
	dpu.readFileData(img_text, data, mark_num);


	vector<LandMark> rotation_data;
	vector<float> hor_data;
	getDatahor(img_text, hor_data);
	int length = hor_data.size();
	for (int i = 0; i < length; i++)
	{
		if (hor_data[i] > 0 && hor_data[i] < 3)
		{
			Mat dst;
			Mat img = imread(img_path + data[i].fileName);
			LandMark landmark(data[i]);
			rotation(25, img, dst, Size(96, 96), landmark, mark_num);
			rotation_data.push_back(landmark);
		}
	}


}


bool getFaceRect(cunpd &pd, int model_id, Mat &dstImg, LandMark & landmark, Rect & rect)
{
	const int widths = dstImg.cols;
	const int heights = dstImg.rows;
	vector<FaceInfomation>  face = pd.detect(dstImg, model_id, 48);
	int length = face.size();
	if (length == 0)
	{
		cout << "not found face ." << endl;
	}
	for (int j = 0; j < length; j++)
	{
		if (face[j].score > 15)
		{
			rect = face[j].rect;


			if (landmark.points[0].x > rect.x && landmark.points[0].x < rect.x + rect.width
				&& landmark.points[0].y > rect.y && landmark.points[0].y < rect.y + rect.height
				&&landmark.points[12].x > rect.x && landmark.points[12].x < rect.x + rect.width
				&& landmark.points[12].y > rect.y && landmark.points[12].y < rect.y + rect.height
				&&landmark.points[16].x > rect.x && landmark.points[16].x < rect.x + rect.width
				&& landmark.points[16].y > rect.y && landmark.points[16].y < rect.y + rect.height
				&&landmark.points[20].x > rect.x && landmark.points[20].x < rect.x + rect.width
				&& landmark.points[20].y > rect.y && landmark.points[20].y < rect.y + rect.height
				&& (abs(landmark.points[7].y - landmark.points[17].y) > (rect.height / 6.0)))
			{
				int rect_w = rect.width;
				int rect_h = rect.height;
				rect.width = rect_w * ZOOM_;
				rect.height = rect_h * ZOOM_;
				rect.x = max(rect.x - (ZOOM_ - 1.0) * rect_w / 2.0, 0.0);
				rect.y = max(rect.y - (ZOOM_ - 1.0) * rect_h / 2.0, 0.0);


				if (rect.x + rect.width > widths)
				{
					rect.width = widths - rect.x;
				}
				if (rect.y + rect.height > heights)
				{
					rect.height = heights - rect.y;
				}
				return true;
			}
		}
	}
	return false;


}


void getoffsetRect(Rect & rect, vector<Rect> & all_rect, int cols, int rows, int max_offset)
{
	srand((unsigned)time(NULL));
	Rect rect0(rect), rect1(rect);
	int offsetx = rand() % max_offset + 1;
	int offsety = rand() % max_offset + 1;


	if (rect.x > offsetx && rect.y > offsety)
	{
		rect0.x = rect.x - offsetx;
		rect0.y = rect.y - offsety;
	}


	offsetx = rand() % max_offset + 1;
	offsety = rand() % max_offset + 1;


	if (rect.x + rect.width + offsetx < cols && rect.y + rect.height + offsety < rows)
	{
		rect1.x = rect.x + offsetx;
		rect1.y = rect.y + offsety;
	}
	all_rect.push_back(rect0);
	all_rect.push_back(rect1);
}


#define NEED_LANDMARK 5
#define CURRENT_LANDMARK 21
const int five_points[5] = { 7, 10, 14, 17, 19 };
string search_base = "H:\\UMD\\";
string search_dir_[] = { search_base + "umdfaces_batch1", search_base + "umdfaces_batch2", search_base + "umdfaces_batch3" };


string text_file[] = { search_base + "umdfaces_batch1\\umdfaces_batch1_ultraface.csv", search_base + "umdfaces_batch2\\umdfaces_batch2_ultraface.csv", search_base + "umdfaces_batch3\\umdfaces_batch3_ultraface.csv" };
string text_pre[] = { "batch1_aug_", "batch2_aug_", "batch3_aug_" };
string tail_[] = { ".jpg", ".jpg" , ".jpg" };


string base = search_base + "landmark_5\\augment_img\\";
string result_img = base + "result_img_" + to_string(SIZE) + "\\";
string result_txt = base + "landmark_" + to_string(SIZE) + "_5.txt";
const int theta_offset = 5;
const int theta_max = 20;
vector<LandMark> rotation_point;
int countNum = 0;
bool ready = false;
std::mutex mtx_syn;
std::condition_variable cv_syn;
void roll_yaw_pitch_data(LandMark result_mark, int temp, cunpd &pd, int model_id, DataPrepareUtil &dpu)
{


	float roll = result_mark.direct[2];


	string img_path = search_dir_[temp] + "\\" + result_mark.fileName;
	if (_access(img_path.c_str(), 0) == -1)
	{
		cout << "coun't found filename" << img_path << endl;
		return;
	}
	Mat img = imread(img_path);
	Mat dstImg; //dstImg.create(heights, widths, CV_8UC1);
	cvtColor(img, dstImg, CV_BGR2GRAY);
	//yaw 增强 pitch 增强
	for (int j = 0; j < 2; j++)
	{
		if (result_mark.direct[j] > -theta_offset && result_mark.direct[j] < theta_offset)
		{
			Rect rect;
			LandMark landmark(result_mark);
			bool success = getFaceRect(pd, model_id, dstImg, landmark, rect);
			if (success)
			{
				vector<Rect> all_rect;
				getoffsetRect(rect, all_rect, img.cols, img.rows, 4);
				for (int i = 0; i < 2; i++)
				{
					LandMark dst_landmark;
					//vector<string> filenames;
					//splitString(landmark.fileName, filenames, "/");
					//string filename = filenames[filenames.size()-1];
					
					std::unique_lock<std::mutex> lck(mtx_syn);
					dst_landmark.fileName = text_pre[temp] + to_string(countNum++) + ".png";
					lck.unlock();
					//cout << img.rows << " " << rotat_img.cols << " " << rect.x << " " << rect.y << " " << rect.width << " " << rect.height << endl;


					Mat roi_face = img(all_rect[i]);
					cv::resize(roi_face, roi_face, Size(SIZE, SIZE));
					//坐标转换
					for (int k = 0; k < 5; k++)
					{
						dst_landmark.visible[k] = landmark.visible[five_points[k]];
						dst_landmark.points[k].x = ((float)SIZE / all_rect[i].width) * (landmark.points[five_points[k]].x - all_rect[i].x);
						dst_landmark.points[k].y = ((float)SIZE / all_rect[i].height) * (landmark.points[five_points[k]].y - all_rect[i].y);
					}
					imwrite(result_img + dst_landmark.fileName, roi_face);


					std::unique_lock<std::mutex> lck1(mtx_syn);
					rotation_point.push_back(dst_landmark);
					lck1.unlock();
				}
			}


		}
	}
	// roll 增强
	if (roll > -theta_offset && roll < theta_offset)
	{
		for (int i = -1; i < 2; i = i + 2)
		{
			Mat rotat_img;
			LandMark landmark(result_mark);
			int theta = (rand() % theta_max + theta_offset) * i;
			rotation(theta, img, rotat_img, Size(SIZE, SIZE), landmark, CURRENT_LANDMARK);


			Mat dstImg; //dstImg.create(heights, widths, CV_8UC1);
			cvtColor(rotat_img, dstImg, CV_BGR2GRAY);


			//for (int j = 0; j < CURRENT_LANDMARK; j++)
			//{
			//	circle(rotat_img, Point(landmark.points[j]), 2, Scalar(255, 0, 0));
			//}
			//imshow("img", rotat_img);
			//waitKey(0);


			LandMark dst_landmark;


			//vector<string> filenames;
			//splitString(landmark.fileName, filenames, "/");
			//string filename = filenames[filenames.size()-1];
			std::unique_lock<std::mutex> lck(mtx_syn);
			dst_landmark.fileName = text_pre[temp] + to_string(countNum++) + ".png";
			lck.unlock();
			Rect rect;
			bool success = getFaceRect(pd, model_id, dstImg, landmark, rect);
			if (success)
			{
				//cout << rotat_img.rows << " " << rotat_img.cols << " " << rect.x << " " << rect.y << " " << rect.width << " " << rect.height << endl;
				Mat roi_face = rotat_img(rect);
				cv::resize(roi_face, roi_face, Size(SIZE, SIZE));
				//坐标转换
				for (int k = 0; k < 5; k++)
				{
					dst_landmark.visible[k] = landmark.visible[five_points[k]];
					dst_landmark.points[k].x = ((float)SIZE / rect.width) * (landmark.points[five_points[k]].x - rect.x);
					dst_landmark.points[k].y = ((float)SIZE / rect.height) * (landmark.points[five_points[k]].y - rect.y);
				}
				imwrite(result_img + dst_landmark.fileName, roi_face);


				std::unique_lock<std::mutex> lck(mtx_syn);
				rotation_point.push_back(dst_landmark);


				if (rotation_point.size() > 500)
				{
					dpu.writePointVisibletoFile(result_txt, rotation_point, NEED_LANDMARK);
					rotation_point.clear();
				}
				if (countNum % 500 == 0)
				{
					cout << "prepare data:" << countNum << endl;
				}
				lck.unlock();
			}
		}
	}






}


vector<LandMark> result_point;   //注意 使用多线程 时共同处理的 变量用 全局变量。
void deal_thread(int temp, int model_id, DataPrepareUtil &dpu, cunpd &pd)
{
	while (true)
	{
		std::unique_lock<std::mutex> lck(mtx_syn);
		while (!ready) {
			cv_syn.wait(lck);
		}
		//
		auto itor = result_point.begin();
		auto itor2 = result_point.end();
		if (itor == itor2)
		{
			break;
		}
		LandMark landmark(result_point[0]);
		result_point.erase(itor);
//		cout << "landmark.fileName is:"<<landmark.fileName<< "thread id"<< this_thread::get_id()<< endl;
		lck.unlock();


		roll_yaw_pitch_data(landmark, temp, pd, model_id, dpu);
		
	}


}


void go()
{
	std::unique_lock<std::mutex> lck(mtx_syn);
	ready = true;
	cv_syn.notify_all();
}



int main()
{

	cunpd pd;
	int model_id = pd.AddNpdModel(0);

	/*string img_path = "D:\\UMD\\result_img_96\\";
	string result_path = "D:\\UMD\\arguement_data\\";
	string img_text = img_path + "shutter_96_5_train.txt";
	string result_text = result_path + "augment_96_5_train.txt";
	augment_data(img_path, img_text, result_path, result_text);*/

	string base_dir = base;
	if (_access(base_dir.c_str(), 0) == -1)
	{
		_mkdir(base_dir.c_str());
	}
	string dir = result_img;
	if (_access(dir.c_str(), 0) == -1)
	{
		_mkdir(dir.c_str());
	}


	srand((unsigned)time(NULL));
	DataPrepareUtil dpUtil;

	dpUtil.clearFileData(result_txt);

	long count = 0;

	vector<LandMark> rotation_point;
	for (int temp = 0; temp < 3; temp++)
	{
		long countNum = 0;
		//vector<LandMark> result_point;


		dpUtil.readFileData(text_file[temp], result_point, CURRENT_LANDMARK);


		std::thread threads[4];
		for (int i = 0; i < 4; i++)
		{
			threads[i] = std::thread(deal_thread, temp, model_id, dpUtil, pd);
			//threads[i] = std::thread(threadA, i, result_point, temp, model_id, dpUtil, pd);
		}
		cout << "temp start:" << temp << endl;
		go();
		for (auto &th : threads) {
			th.join();
		}
		cout << "temp end:" << temp << endl;
		
		}
		if (rotation_point.size() > 0)
		{
			dpUtil.writePointVisibletoFile(result_txt, rotation_point, NEED_LANDMARK);
			rotation_point.clear();
		}
		system("PAUSE");
		return 0;
}

Future使用

 

void test_thread() {
    //1.
    std::thread t(foo, "hello");
    t.join();

    //2.
    std::packaged_task<int(int)> task([](int a) {std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "my task" << std::endl; return a; });
    std::future<int> result = task.get_future();
    std::thread(std::move(task), 2).detach();
    std::cout << "Waiting...." << std::endl;
    //result.wait();

    //result.get会阻塞,直到对应线程完成
    std::cout << "Done  result is:" << result.get() << std::endl;

    //3.
    std::packaged_task<string(string)> task1(foo);
    std::future<string> result1 = task1.get_future();
    string str = "liu";
    std::thread(std::move(task1), str).detach();
    //result1.get会阻塞,直到对应线程完成
    std::cout << "task1:" << result1.get() << std::endl;
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++多线程编程 的相关文章

  • VSLAM学习之(一)

    相关资料整理 CVPR 2014 Visual SLAM Tutorial 学习SLAM需要哪些预备知识 VSLAM简介 VSLAM xff08 Visual Simultaneous Localization and Mapping xf
  • 一本书,让我走上编程之路

    好多年了 xff0c 我的书架上一直留着这本书 xff0c 不是因为有多好 xff0c 而是它让我明白了许多 工作后的我读过无数本厚厚薄薄的书 xff0c 其中有的确实十分精彩 但唯一让我真正用心读过的 xff0c 是大学期间一本普通的c
  • ubuntu虚拟机终端(terminal)打不开

    最近想用cmake gui编译opencv xff0c 发现虚拟机上终端 xff08 terminal xff09 打不开 xff0c 点图标也打不开 xff0c ctrl 43 alt 43 t也没反应 然后百度了一下ctrl 43 al
  • Debian 用户名密码输入成功后重复登录

    问题描述 xff1a 在用户名和密码都输入成功的情况下 xff0c 重复出现登录界面 xff0c 无法进入主界面 问题原因 xff1a 原因为修改了 etc environment 环境变量 xff0c 在文件末尾添加了 xff1a PAT
  • C++最佳实践之常用库介绍

    C 43 43 的常用库包括 xff1a algorithm chrono iostream future memory map queue unordered map regex set string sstream stdexcept
  • Android GB905协议详解

    最近发现 xff0c 深圳做网约车和货车的协议的公司越来越多了 xff0c 之前在公司做过一些这方面的项目 就来写个这方面的文章记录下 xff0c 也顺便分享下 GB905 xff0c 主要是面向网约车的一种协议 xff0c 主要监控司机的
  • Python pip 命令清除Python包缓存文件

    Python pip 命令清除Python包缓存文件 命令行窗口中安装包命令调用方式示例 使用pip命令清除Python包缓存文件 命令行窗口中安装包命令 关于如何打开Windows 操作系统下的命令行窗口可以查看这篇 如何在Windows
  • hadoop集群搭建及其组件介绍和目录规划

    搭建HADOOP HA集群 集群角色分配 角色描述角色IP兼职兼职描述NN1NameNode节点ip nn1rmResourceManagerNN2NameNode节点ip nn2his serverJobHistoryServerJN1J
  • ubuntu12.04环境下Floodlight+mininet搭建OpenFlow测试平台

    注 xff1a 此笔记为在学习SDN相关知识时的个人总结 xff0c 如需转载麻烦表明出处 xff0c 并附上连接 xff08 http blog csdn net sherkyoung article details 23540017 x
  • DERBY数据库环境搭建以及简单使用

    1 derby数据库 Apache Derby 项目的目标是构建一个完全用 Java 编程语言编写的 易于使用却适合大多数应用程序的开放源码数据库 特点 xff1a l 程序小巧 xff0c 基础引擎和内嵌的JDBC 驱动总共大约 2MB
  • 如何做好项目各干系人的管理及应对?

    如何更好地识别 分析和管理项目关系人 xff1f 主要有以下几个方面 xff1a 1 项目干系人的分析 一般对项目干系人的分析有2种方法 xff0c 方法一 xff1a 权利 xff08 影响 xff09 xff0c 即对项目可以产生影响的
  • floodlight添加模块实验

    元旦的时候发现floodlight 居然更新了 xff0c 吓坏我了 V0 9 是 12 年 10 月更新的 xff0c 然后在 14 年 12 月 30 日连续发布了 V0 91 和 V1 0 OTZ 根据release note 来看
  • OpenFlow1.3协议解析

    注 xff1a 此笔记为在学习OpenFlow协议时的个人总结 xff0c 存在诸多错误和不完善的地方 xff0c 后续会陆续完善 xff0c 仅作参考 如需转载麻烦表明出处 xff0c 并附上连接 xff08 http blog csdn
  • [python]从零开始学python——颜色的16进制于RGB之间的转换

    在学习openstack的时候 xff0c 发现openstack是python开发的 xff1b 学习mininet自定义拓扑 xff0c 发现mininet是python开发的 xff1b 看看ryu xff0c 还是python开发的
  • ubuntu14.04 升级内核

    转载地址 xff1a http blog csdn net u011884290 article details 52082809 前两周升级了ubuntu 16 04 出现了关机卡死的情况 xff0c 查了下可能是系统内核 xff08 4
  • linux firefox提示“firefox is already running”的解决方法

    背景解决方法 背景 linux下 xff0c 多用户通过vnc访问指定IP xff0c 比如192 168 2 94 不同用户执行firefox xff0c firefox进程存在且仅能存在一个 记录一下 xff0c 以备不时之需 解决方法
  • 论C++类对象赋值

    class Demo public Demo Demo int j cout lt lt 34 Demo int 34 lt lt j lt lt endl this gt i 61 j Demo const Demo amp d cout
  • gnuradio+b210实现FM收音机

    gnuradio 43 b210实现FM收音机 环境介绍FM接收1 Flow Graph2 变量参数 FM发射1 Flow Graph2 变量参数 环境介绍 NameVersiongnuradio3 7 14 0uhd3 15 0 0ubu
  • 图深度学习 Deep Learning on Graph

    深度学习在图上的应用 引言图神经网络图卷积网络卷积操作谱方法Efficiency AspectMultiple Graphs Aspect框架 Readout 操作改进与讨论注意力机制残差和跳跃连接 Residual and Jumping
  • Ubuntu16.04中文输入法安装初战

    最近刚给笔记本装了Ubuntu 43 win10双系统 xff0c 但是ubuntu16 04没有自带中文输入法 xff0c 所以经过网上的一些经验搜索整合 xff0c 分享一下安装中文输入法的心得 本文主要介绍了谷歌拼音跟ibus中文输入

随机推荐

  • GoDaddy与Namecheap域名注册商对比分析

    默默鸟已经有几天没有更新博客 xff0c 博客更新的少是因为我必须在更新之前想好了围绕博客的主题更新 xff0c 而不是想到哪个就更新哪些内容 xff0c 一来可能不符合博客的中心 xff0c 二来对于用户群也有影响 xff0c 同事显得不
  • ubuntu18安装vnc远程桌面服务

    安装 vnc4server xff0c xfce4 sudo apt install vnc4server xfce4 xfce4 goodies 安装完成后配置VNC登录密码 vncpasswd 启动VNC server vncserve
  • 国内国外常用的10个云服务器可视化管理面板

    如今无论是搭建网站 xff0c 还是部署小程序 xff0c 甚至一些企业应用都会用到云服务求或者独立服务器 对于很多希望利用网站 网络创业的 xff0c 也会用到服务器 xff0c 不过在使用服务器过程中 xff0c 我们对于服务器环境的配
  • 几个Windows强力卸载工具软件推荐

    对于我们有在使用Windows系统的时候 xff0c 是不是会主动或者被动的安装一些软件和插件工具 殊不知日积月累之后 xff0c 系统中的软件会越来越多 xff0c 甚至有很多我们安装几个月甚至几年都不会用到的 这些软件 xff0c 其实
  • 几款值得选的SSH客户端软件

    对于服务器运维工作来说 xff0c 我们少不了SSH远程客户端管理工具 我们在用哪款呢 xff1f 比如常见用的包括PuTTY XShell WindTerm等 xff0c 有很多的付费或者免费的 xff0c 既然有这么多免费且好用的为什么
  • 原生态Ubuntu部署LAMP环境 PHP8.1+MySQL+Apache

    如果我们部署WEB环境用于网站项目 xff0c 我们还是建议用成熟的一键包或者可视化面板这种 xff0c 毕竟软件的部署和后续的运维方便很多 但是 xff0c 如果我们有需要学习Linux环境的原理 xff0c 那还是要学会原生态部署软件的
  • Passwork适合多人协作团队的自建密码管理器

    如今互联网已经深入我们的工作和生活中 xff0c 从办公 购物 学习每天都会用到各种网站平台 各种APP客户端 各种软件账户 xff0c 这就离不开对各个平台账户的管理 我们应该也知道 xff0c 账户的安全是至关重要的 xff0c 如果账
  • 完整利用Rsync实现服务器/网站数据增量同步备份

    我们在选择VPS 服务器架设项目之后 xff0c 所有的项目 网站数据都需要我们自行备份和维护 xff0c 即便有些服务商有提供管理型服务器 xff0c 但是数据自行备份和管理才是较为靠谱的 无论是网站 xff0c 还是其他项目 xff0c
  • 整理Nginx/Apache服务器配置HTTPS SSL证书示范教程

    昨天我们看到百度发布 34 百度烽火算法升级 34 xff0c 提到网站如果被劫持或者拦截可能会降低网站的权重和排名等问题 这使得我们网站需要使用HTTPS SSL证书来减少被拦截劫持的风险 其实在早些时候我们已经看到很多浏览器都强制要求网
  • 6个免费DNS解析服务商评测分析 适用于网站域名解析应用

    这几天我们很多网友应该知道CloudXNS DNS解析服务商预计7月15日会宣布停止提供免费解析服务而主营商业服务 虽然网络上提供免费DNS解析服务商很多 xff0c 但是毕竟这么多年CloudXNS域名解析稳定性还是不错的 xff0c 而
  • 两种方法修改数据库myslq密码

    搞了很久终于修改数据库密码成功了 命令行修改root密码 xff1a mysql gt UPDATE mysql user SET password 61 PASSWORD 新密码 WHERE User 61 root mysql gt F
  • 关于学生课堂行为识别算法

    目前基于针对学校做了一款考生行为识别算法 xff0c 算法可以在服务器部署 xff0c 也可以在前端设备如Jetson RK等边缘设备运行 xff0c 目前算法已经投入使用 xff0c 算法效果如下 目前算法在 2080Ti 服务器运行效率
  • 获取imagefield 类型图片的路径

    绝对路径 request build absolute uri 图片 url 相对路径 图片 url
  • mmdetection 常用命令

    1 多卡训练 CUDA VISIBLE DEVICES 61 0 1 2 3 PORT 61 15200 tool dist train py configs py 4 2 普通测试 python tools test py configs
  • yolov5 导出onnx 忽略检测层

    def forward self x z 61 inference output for i in range self nl x i 61 self m i x i conv bs ny nx 61 x i shape x bs 255
  • python opencv 添加运动模糊

    在训练过程中增加 运动模糊 class MotionBlur object def init self p 61 0 5 degree 61 5 angle 61 45 self p 61 p self degree 61 degree s
  • pth 多类模型改成一类模型

    import torch import copy def change pth input pth out pth model dir 61 input pth checkpoint 61 torch load model dir mode
  • 使用opencv进行车牌提取及识别

    商业合作可联系 xff1a 547691062 64 qq com 目录 1车牌提取过程 1 1车辆图像获取1 2车牌定位1 3车牌字符分割2车牌提取 2 1灰度化2 2Candy边缘检测2 3形态学 xff08 膨胀腐蚀 xff09 处理
  • python 爬虫禁止访问解决方法(403)

    1 增加Header2 代理IP3 终极方法4 实例练习 5 更多思考 在上一篇博客中说到 xff0c 程序使用一段时间后会遇到HTTP Error 403 Forbidden错误 因为在短时间内直接使用Get获取大量数据 xff0c 会被
  • C++多线程编程

    c 43 43 11 之后有了标准的线程库 xff1a C 43 43 11发布之前 xff0c C 43 43 并没有对多线程编程的专门支持 xff0c C 43 43 11通过标准库引入了对多线程的支持 xff0c 大大方便了程序员的工