The share & atomic operation of std::future is used to process multi-threaded acquisition and write shared data

1. std::future is
used to obtain the return value of the asynchronous thread calculation result.
The get function design of the future is move semantics (move), and it is cleared after the move, so the data can only be get once

int mythread()
{
    
    
cout<<"mythread starat ,ThreadID is"<<std::this_thread::get_id()<<endl;
std::chrono::millseconds dura(5000);
std::this_thread::sleep_for(dura);
cout<<"mythread end,ThreadID is<<std::this_thread::get_id()<<endl; 
return 5;
}

int main()
{
    
    
cout<<"main thread start,threadId is "<<std::this_thread::get_id()<<endl;
std::future<int> result=std::async(thread);
cout<<"Go on!"<<endl;
//The program will be stuck,until the result get the return value. 
cout<<result.get()<<endl;
cout<<"Main finish"<<endl;

return 0;
}

2.std:future_status::timeout & std::future_status::ready is
an enumerated type, used to mark and determine whether the thread is running overtime and completed

int mythread()
{
    
    
cout<<"mythread starat ,ThreadID is"<<std::this_thread::get_id()<<endl;
std::chrono::millseconds dura(5000);
std::this_thread::sleep_for(dura);
cout<<"mythread end,ThreadID is<<std::this_thread::get_id()<<endl; 
return 5;
}

int main()
{
    
    
cout<<"main thread start,threadId is "<<std::this_thread::get_id()<<endl;
std::future<int> result=std::async(mythread);
cout<<"Go on!"<<endl;
//1.main thread wait 8 seconds
std::future_status mstatus=result.wait_for(std::chrono::seconds(8));
//In 8 seconds,mythread finish & return the value ,mstatus != timeout,=ready

//2.main thread wait 1 seconds
//std::future_status mstatus=result.wait_for(std::chrono::seconds(1));
//In 1 seconds,mythread doesn't finish & return the value ,mstatus == timeout


if(mstatus==future_status::timeout)
{
    
    
	cout<<"Mythread doesn't finish"<<endl;
}
else if(mstatus==future_status::ready)
{
    
    
	cout<<"Mythread finish"<<endl;
	cout<<result.get()<<endl;
}

return 0;
}

3.future_status::deferred
delay triggers running mythread, but running the mythread function in the main thread is equivalent to a function call.

int main()
{
    
    
cout<<"main thread start,threadId is "<<std::this_thread::get_id()<<endl;
std::future<int> result=std::async(std::launch::deferred,mythread);
cout<<"Go on!"<<endl;
//1.main thread wait 8 seconds
std::future_status mstatus=result.wait_for(std::chrono::seconds(8));
//In 8 seconds,mythread finish & return the value ,mstatus != timeout,=ready

if(mstatus==future_status::timeout)
{
    
    
	cout<<"Mythread doesn't finish"<<endl;
}
else if(mstatus==future_status::ready)
{
    
    
	cout<<"Mythread finish"<<endl;
	cout<<result.get()<<endl;
}

else if(mstatus==std::future_status::deferred)//延迟
{
    
    
	//if async的第一次参数被设置为deferred,则成立
	cout<<"Thread defer<<endl;
	cout<<result.get()<<endl;//当运行到get时,mythread才开始执行。wait_for失效
}


return 0;
}

4. std::shared_future
is also a class template.
The get() function of std::future moves data.
The get() function of std::shared_future copies data. Multiple threads (mythread2&mythread3) can obtain data.

(1) Determine whether the shared data exists and then share

//判断型
int mythread(int mypar)
{
    
    
	cout << "mythread starat ,ThreadID is" << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "The para is " << mypar << endl;
	cout << "mythread end,ThreadID is"<<std::this_thread::get_id()<<endl; 
	return 5;
}

void mythread2(std::shared_future<int>& tmpf)
{
    
    
	cout << "mythread2 start, threadID is" << std::this_thread::get_id() << endl;
	auto result = tmpf.get();
	cout << "mythread2 result=" << result << endl;
}

void mythread3(std::shared_future<int>& tmpf)
{
    
    
	cout << "mythread3 start, threadID is" << std::this_thread::get_id() << endl;
	auto result = tmpf.get();
	cout << "mythread3 result=" << result << endl;
}

int main()
{
    
    
	cout << "main thread start,threadId is " << std::this_thread::get_id() << endl;
	std::packaged_task<int(int)>mypt(mythread);
		std::thread t1(std::ref(mypt), 101);
	t1.join();
	std::future<int>result = mypt.get_future();

	bool ifcanget = result.valid();//vaild判断result中是否含有值,有true,没有false
	if (ifcanget == true)
	{
    
    
		//std::shared_future<int>result_s(std::move(result));//通result.share()
		std::shared_future<int>result_s(result.share());//把result的值移动到result_s中去
		auto mythreadresult = result_s.get();
		std::thread t2(mythread2, ref(result_s));
		t2.join();
		std::thread t3(mythread3, ref(result_s));
		t3.join();
	}

	return 0;
}

(2) Do not judge whether there is a direct share of the shared data.

//不判断,直接share。
int main()
{
    
    
	cout << "main thread start,threadId is " << std::this_thread::get_id() << endl;
	std::packaged_task<int(int)>mypt(mythread);
	std::thread t1(std::ref(mypt), 101);
	t1.join();
	//std::future<int>result = mypt.get_future();

	std::shared_future<int> result_s(mypt.get_future());
	auto mythreadresult = result_s.get();

	thread t2(mythread2, std::ref(result_s));
	thread t3(mythread3, std::ref(result_s));
	t2.join();
	t3.join();
	cout << "Result_s.get() is" << mythreadresult << endl;

	return 0;
}

5. std::atomic
atomic operation (inseparable operation), multi-threaded concurrency mode without lock technology, program execution fragments that will not be interrupted in multi-threading.
Mutex locking is generally for a code segment , while atomic operations are for a variable , not a code segment.
Generally used to count the number of data sent and received.

Problem code, when two-threaded execution uses the same function to perform large data operations on global variables, because of thread slice switching, the expected operation cannot be performed completely, and there will be insufficient data. (Reading the intermediate value problem) The
final result of g_count is expected to be 2000000, and the actual result is about 130,000.

static int g_count=0;
void mythread()
{
    
    
	for (int i = 0; i < 1000000; i++)
	{
    
    
		g_count++;
	}
}


int main()
{
    
    
	std::thread t1(mythread);
	std::thread t2(mythread);

	t1.join();
	t2.join();

	cout << "G_count is " << g_count << endl;
	return 0;
}

Median?
I wrote a test code. The
results show that regardless of thread 1 or thread 2, i can be added to 9999, but the result is not 2W. Then the problem with the intermediate value is that when the threads are switched, the g_count of 1 thread is 200, the time slice is switched to 2 threads, and the g_count of 2 threads is counted to 100, and the time slice is switched. But at this time, the g_cout count of 1 thread starts from 100, and 100 counts are lost. This is the middle value problem.

std::mutex g_mutex;
void mythread()
{
    
    
	for (int i = 0; i < 10000; i++)
	{
    
    
		//cout << "thread is" << _threadid << ", i is" << i << endl;
		
		//g_mutex.lock();
		g_count++;
		if (i > 9990)
		{
    
    
			cout << "ThreadId is " << _threadid << ", i is " << i << endl;
		}
		//g_mutex.unlock();
	}
}

int main()
{
    
    
	std::thread t1(mythread);
	std::thread t2(mythread);

	t1.join();
	t2.join();

	

	cout << "G_count is " << g_count << endl;
	return 0;
}

Solution 1: Mutex, low efficiency

static int g_count=0;
std::mutex g_mutex;
void mythread()
{
    
    
	for (int i = 0; i < 1000000; i++)
	{
    
    
		g_mutex.lock();
		g_count++;
		g_mutex.unlock();
	}
}


int main()
{
    
    
	std::thread t1(mythread);
	std::thread t2(mythread);

	t1.join();
	t2.join();

	cout << "G_count is " << g_count << endl;
	return 0;
}

Solution 2: Atomic operation std::atomic, high efficiency
C++11, a class template.

std::atomic<int> g_count = 0;
void mythread()
{
    
    
	for (int i = 0; i < 1000000; i++)
	{
    
    
		g_count++;
	}
}
int main()
{
    
    
	std::thread t1(mythread);
	std::thread t2(mythread);

	t1.join();
	t2.join();

	cout << "G_count is " << g_count << endl;
	return 0;
}

Atomic operations do not support binocular operators! !
+, -, *, / are not supported, and abnormal data will be returned.

void mythread()
{
    
    

	for (int i = 0; i < 100000; i++)
	{
    
    
		g_count++;//输出200000,正确
		g_count += 1;//输出200000,正确
		g_count = g_count + 1;//错误,不是所有运算符都支持原子操作
		//一般原子操作,针对++,--,+=,&=,|=是支持的
	}
}

Bool type atomic is
used to control the code segment

std::atomic<bool>g_ifend = false;//防止多线程同时写入数据时乱套。
void mythread()
{
    
    
	std::chrono::milliseconds dura(1000);
	while (g_ifend == false)
	{
    
    
		//如果为false,则系统未要修此线程退出,线程继续执行。
		cout << "Thred ID is " << _threadid << endl;
		std::this_thread::sleep_for(dura);
	}
	cout << "Thread  end, ID is " << _threadid << endl;
	return;
}
int main()
{
    
    
	std::thread t1(mythread);
	std::thread t2(mythread);
	
	std::chrono::seconds dura(5);
	std::this_thread::sleep_for(dura);
	//因为是线程中运行的是while循环,如果结束条件在join()语句下方,那么会因为joim阻塞语句,产生死循环
	//所以当线程需要,函数外判定结束时,判定语句一定要在join之前执行。
	g_ifend = true;//让原子对象,自行了断。

	t1.join();
	t2.join();

	return 0;
}

Result graph

Guess you like

Origin blog.csdn.net/guanxunmeng8928/article/details/108559570