参考: 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.cn/185144.html原文链接:https://javaforall.cn