C++多线程通信_c++ socket 多线程

C++多线程通信_c++ socket 多线程参考:https://m.imooc.com/article/289630C++11标准库新引入的线程库https://www.jianshu.com/p/e5a3498ba930(一)多线程编程#include<iostream>#include<thread>#include<mutex>#include<condi…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

参考:
https://m.imooc.com/article/289630   C++11 标准库新引入的线程库
https://www.jianshu.com/p/e5a3498ba930

(一)多线程编程

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <csignal>
#include <windows.h>

using namespace std;

std::mutex mlock;
std::condition_variable cv;
bool ready = false;

int thread_entry(int a, char y)
{
	std::thread::id  id = std::this_thread::get_id();
	cout << "this thread id is " << id << endl;
	while (!ready) {
		std::this_thread::yield();//交出本线程的时间片使用权,等待下一次调度.可作为一种同步机制。
	}
	cout << "args:" << a << "\t" << y << endl;
	
	return 0;
}
int thread_entry_uselock(int a, char c)
{	
	static int count = 0;
	std::thread::id id = std::this_thread::get_id();
	cout << "this thread id is " << id << endl;
	mlock.lock();
	for (int i = 0; i < 300000; ++i)
		count++;
	cout << "count = " << count << endl;//300000时count出现不是300000倍数的情况,
										//说明多线程在访问count的时候有问题了,加锁就OK!
	mlock.unlock();
	
	return 0;
}
int thread_entry_uselock_usecv(int a, char c)
{
	static int count = 0;
	std::thread::id id = std::this_thread::get_id();
	cout << "this thread id is " << id << endl;

	std::unique_lock<std::mutex> lock(mlock);
	while(!ready)
		cv.wait(lock);//会先解锁,陷入休眠,让其他竞争此锁mlock的线程得以继续执行;当比唤醒后再上锁
	//cv.wait(lock, []() {return ready; });//使用谓词
	for (int i = 0; i < 300000; ++i)
		count++;
	cout << "count = " << count << endl;

	return 0;
}

int thread_entry_with_future_promise(std::future<int> *future)
{
	std::thread::id id = std::this_thread::get_id();
	cout << "this thread id is " << id << endl;
	
	int value = future->get();
	cout << "value = " << value << endl;

	return 0;
}
int add(int a, int b)
{
	return (a + b);
}
int thread_entry_with_future_packaged_task(std::future<int> *future)
{
	std::thread::id id = std::this_thread::get_id();
	cout << "this thread id is " << id << endl;

	int value = future->get();
	cout << "value = " << value << endl;

	return 0;
}

int main(int argc, char **argv)
{
	cout << "****************demo1创建单个线程***************************" << endl;
	std::thread th(thread_entry, 1, 'a');//创建线程,并传参
	cout << "ready = true" << endl;
	ready = true;
	if ( th.joinable() )//判断th这个线程是否可连接
		th.join();//阻塞连接
	else
		th.detach();//分离
	
	ready = false;
	cout << "****************demo2创建多个线程***************************" << endl;
	std::thread thmore[5];
	for (int i = 0; i < 5; ++i) {
		thmore[i] = std::thread(thread_entry, i, 'b');
	}
	cout << "ready = true" << endl;
	ready = true;
	for (int i = 0; i < 5; ++i) {
		thmore[i].join();//阻塞连接
	}

	ready = false;
	cout << "****************demo3使用mutex***************************" << endl;
	std::thread th_uselock[5];
	for (int i = 0; i < 5; ++i) {
		th_uselock[i] = std::thread(thread_entry_uselock, i, 'c');
	}
	cout << "ready = true" << endl;
	ready = true;
	for (int i = 0; i < 5; ++i) {
		th_uselock[i].join();//阻塞连接
	}

	ready = false;
	cout << "****************demo4使用mutex + condition_variable*************" << endl;
	std::thread th_uselock_usecv[5];
	for (int i = 0; i < 5; ++i) {
		th_uselock_usecv[i] = std::thread(thread_entry_uselock_usecv, i, 'd');
	}
	char c;
	cout << "ready?" << endl;
	cin >> c;
	if ( (c == 'Y') || (c =='y') ) {
		ready = true;
		//cv.notify_one(); //随机唤醒一个等待的线程
		cv.notify_all();   //唤醒cv上等待的所有线程,应避免惊群效应
	}
	else
		cout << "Now is not ready!" << endl;
	
	for (int i = 0; i < 5; ++i) {
		th_uselock_usecv[i].join();//阻塞连接
	}

	ready = false;
	cout << "****************demo5使用comm with future-promise*************" << endl;
	
	std::promise<int> promise;
	std::future<int> future = promise.get_future();
	std::thread thread_future_promise = std::thread(thread_entry_with_future_promise, &future);
	
	promise.set_value(int(2));

	thread_future_promise.join();

	cout << "****************demo6使用comm with future-packaged_task*************" << endl;

	std::packaged_task<int(int, int)> task(add);
	std::future<int> future1 = task.get_future();
	std::thread thread_future_packaged_task = std::thread(thread_entry_with_future_packaged_task, &future1);
	
	Sleep(3000);//3s
	std::thread thread_task(std::move(task), 2, 5);


	thread_task.join();
	thread_future_packaged_task.join();

	cout << "****************demo7使用comm with async*************" << endl;
	auto f = std::async(std::launch::async, add, 3, 3);
	cout << "async f.get = " << f.get() << endl;

	return 0;
}

(二)线程间通信的三种方式:共享内存、管道通信(Linux)、future通信机制
1.共享内存
多线程会共享全局变量区,所以可以多个线程去option 这个临界区的XXX;
但是通常  共享内存会引发不安全的结果  ==》所以就有了一些保护机制:互斥锁mutex、条件变量cv、原子操作和线程局部存储等。

2.管道通信(Linux)
如:
int fd[2];
pipe(fd);  
将int fd[2]置为全局,fd[0]为读端口 另一个为写端口。

就可以:一个线程向fd[1] write,一个线程向fd[0] read。
 
Note:与进程间通信的不同,进程间通信时,子进程会copy父进程的fd,故两端要各关闭一个读写。

3.future通信机制
头文件<future>

std::future 可以和 std::promise配对,形成信道传输data或异常。
std::future 可以和 std::packaged_task  配对,形成信道传输data或异常。

基于生产者和消费者模型:
(1)std::future 可以和 std::promise配对
std::future 是消费者,使用来自生产者的数据;
std::promise是生产者,产生数据,并给予消费者。

这样用:
#include <future>

std::promise<int> promise;
std::future future = promise.get_future();//match

int x = 2;
std::promise.set_value(x);//将x的值送入信道发送出去——1
int y = std::future.get();//信道阻塞接收数据————-2

在多线程中使用:
把上面的—-1  ——2在不同线程中使用就ok。

Note:
(1)future通信机制的根本是依赖:配对的两端共享状态:—-1将状态设置为ready,—-2就可以读了,否则—-2陷入阻塞。
(2)若promise在被销毁前都为set_value,那么promise被销毁后,future将引发异常:
    try{

        cout << future.get(); << endl;    
    }
    catch(std::future_error &e){

        cerr << e.code << e.what() << endl;
    }
(3)promise既可以发送有用的数据:promise.set_value(x),也可以发送一个异常:promise.set_execption(e)
// 自定义异常需要使用make_exception_ptr转换一下
    promise.set_exception(   std::make_exception_ptr(   std::logic_error(“caught”)    )    );

(4)std::promise<void>;用于通知配对的future.get()接触阻塞。此时,promise.set_value()不接受任何参数;
(5)在线程退出时future.get()才得到promise发送的info:
    std::promise.set_value_at_thread_exit(x);
    std::promise.set_excption_at_thread_exit(e);
(6)promise.set_value();发送一次后,状态变为ready,在ready状态下不能再次set_value;
(7)get_future仅能调用一次,多次调用会触发std::future_error异常。

(2)std::future 可以和 std::packaged_task  配对
std::future 是消费者,使用来自生产者的数据;
std::paclaged_task是生产者,产生数据,并给予消费者。

这样用:
#include <future>

int func(int a, char c){ /*opt*/ return 0;}

std::packaged_task< int(int, char) > task(func);//
std::future<int> future = task.get_future();  //match

int x = 2; char c = ‘a’;
std::thread th(std:move(func), x, c);

/*
task.reset();
std::future<int> future = task.get_future();  //match again
std::thread th(std:move(func), x, c);
*/

Note:
(1)原理仍然是:状态共享;
(2)romise.set_value();设置后状态为ready就不能再次往信道中传输数据了,但是std::packaged_task可以多次传输,因为其内有个reset成员函数。
(3)task.valid();可以判断task当前是否可用:
    if( task.valid() )    return ture;
    else            return false;
(4)std::make_ready_at_thread_exit();设置在线程退出时让future.get()接受数据;
(5)std::packaged_task设计是目的是让future.get() 异步得到task对象函数的返回值。

(3)std::async <类似于std::packaged_task>
std::async的出现大大减轻了异步的工作量。使得一个异步调用可以像执行普通函数一样简单。

int func(int a, char c){ /*opt*/ return 0;}
int x = 2; char c = ‘a’;
auto f = std::async(/*策略*/std::lunch::async, func, x, c);
int y = f.get();

不同的策略会让func有不同的执行策略:
enum class launch {

    // 保证异步行为,F将在单独的线程中执行
    async = 1,
    // 当其它线程调用std::future::get时,
    // 将调用非异步形式, 即F在get函数内执行
    deferred = 2,
    // F的执行时机由std::async来决定
    any = async | deferred
};

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/185144.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 我们做出了一个艰难的决定[通俗易懂]

    我们做出了一个艰难的决定[通俗易懂]经过半年多的考虑和准备,前天晚上,我们做出了一个艰难的决定:让大儿子在家读书。我厌倦了孩子题海战术,买的课外书根本没有时间读,而他的身心健康变得越来越糟糕了。我知道有很多的理由可以让孩子继续读书,譬如

    2022年7月1日
    23
  • BaseAdapter导致notifyDataSetChanged()无效的四个原因及处理方法

    BaseAdapter导致notifyDataSetChanged()无效的四个原因及处理方法前一段时间在做一个项目的时候遇到了一个关于BaseAdapter的notifyDataSetChanged()方法无效问题,当时在网上搜了一个解决方法,今天又遇到了一个类似的问题,我在这里做个记录,防止以后再次发生,或者其他朋友再次遇到。一、ScrollView中嵌套ListView或GridView原因:两个的滚动监听冲突解决方法:重写ListView或GridViewpackagecom.m

    2022年6月18日
    24
  • SQL学习(一):months_between日期函数

    SQL学习(一):months_between日期函数日期函数months_between的用法:MONTHS_BETWEEN(date1,date2)用于计算date1和date2之间有几个月。如果date1在日历中比date2晚,那么MONTHS_BETWEEN()就返回一个正数。如果date1在日历中比date2早,那么MONTHS_BETWEEN()就返回一个负数。如果date1和date2日期一样,那么MONTHS_BETWEEN()就返回一个0。案例SQL>selectmonths_between(to_date(

    2022年8月20日
    8
  • Linux zip加密压缩「建议收藏」

    Linux zip加密压缩「建议收藏」不加密:zip-r压缩文件.zip待压缩文件加密:zip-r-P’密码’压缩文件.zip待压缩文件

    2022年10月21日
    2
  • Mit6.S081-实验1-Xv6 and Unix utilities

    Mit6.S081-实验1-Xv6 and Unix utilitiesMit6.S081-实验1-Xv6andUnixutilities前言一、Bootxv61,实验目的2,操作流程1)切换到xv6-labs-2020代码库的lab1分支2)启动xv63)测试xv64)过程分析5)其他操作二、在xv6中添加一个自己编写的程序1,源码准备2,编译配置3,测试添加程序4,过程分析三、xv6中shell简析前言一、Bootxv61,实验目的利用qemu启动xv62,操作流程1)切换到xv6-labs-2020代码库的lab1分支gitcheckoutut

    2022年9月26日
    2
  • java二维数组试题_Java二维数组及习题总结

    java二维数组试题_Java二维数组及习题总结二维数组二维数组:就是一个由行和列组成的一个矩阵(Matrix);在这个矩阵中访问元素时,是根据元素的行角标和列角标所确定的。二维数组在内存中的存储:无论是二维数组,还是多维数组,它们本身就是一个一维数组,只不过该一维数组中的每一个元素是另一个一维数组。二维数组的创建:int[][]matrix=newint[3][4]———创建一个3行4列的二维数组,元素默认都是0;int[]…

    2022年5月29日
    41

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号