C++多线程

C++对多线程新加的支持操作

 

线程池

  我们有两种常见的创建线程的方法,一种是继承Thread类,一种是实现Runnable的接口,Thread类其实也是实现了Runnable接口。但是我们创建这两种线程在运行结束后都会被虚拟机销毁,如果线程数量多的话,频繁的创建和销毁线程会大大浪费时间和效率,更重要的是浪费内存,因为正常来说线程执行完毕后死亡,线程对象变成垃圾!那么有没有一种方法能让线程运行完后不立即销毁,而是让线程重复使用,继续执行其他的任务哪?我们使用线程池就能很好地解决这个问题。

 直接贴代码吧,代码注释比较详细:

ThreadPool.h

#pragma
once #include <iostream> // std::cout #include <functional> // std::ref #include <thread> // std::thread #include <future> // std::promise, std::future #include <mutex> #include <queue> #include <condition_variable> #include <atomic> #include <cassert> using namespace std; #define MAXTHREADNUM 10 // 设置最大线程数量 typedef function<void()> Task; // 任务模板函数 namespace ThreadPool { class ThreadPool { private: queue<Task> tasks_queue; // 任务队列 vector<thread> thread_pool; // 线程池 atomic<bool> is_stop; // 释放关闭提交 atomic<int> idle_thread_num; // 空闲线程数量 mutex mu; // 互斥锁 condition_variable cv; // 条件变量 /* 公有方法 */ public: // 构造函数: ThreadPool(int size = MAXTHREADNUM) :is_stop(false) { size = size > MAXTHREADNUM ? MAXTHREADNUM : size; idle_thread_num = size; for (int i = 0; i < size; ++i) { // 在线程池中创建指定数量的线程,这里用emplace是为了提供效率,相比insert,emplace不必创建中间的临时变量 thread_pool.emplace_back(&ThreadPool::scheduler, this); } } // 析构函数: ~ThreadPool() { close(); // 首先停止线程池的提交 while (!tasks_queue.empty()) // 将任务队列清空 { tasks_queue.pop(); } cv.notify_all(); for (auto& th : thread_pool) // 结束前将所有线程join,阻塞主进程,防止线程中用到共享资源 { if (th.joinable()) th.join(); } thread_pool.clear(); // 清空线程池 } // 重新打开线程池: void re_open() { if (is_stop.load())is_stop.store(false); cv.notify_all(); // 唤醒所有线程 } // 关闭线程池: void close() { if (!is_stop.load())is_stop.store(true); } // 是否已停止提交任务: bool is_closed() const { return is_stop.load(); } // 获取空闲线程数量: int get_idle_thread_num() { return idle_thread_num; } // 获取任务队列size: rsize_t get_task_num() { return tasks_queue.size(); } // 提交新任务到任务队列: template<class F, class... Args> auto submit(F&& f, Args&&... args)->std::future<decltype(f(args...))> { if (is_stop.load()) { throw std::runtime_error("ThreadPool is closed, can not submit task."); } // 对提交的任务函数与 task 类型做适配 using RetType = decltype(f(args...)); // typename std::result_of<F(Args...)>::type, 函数 f 的返回值类型 std::shared_ptr<std::packaged_task<RetType()>> task = std::make_shared<std::packaged_task<RetType()>>( std::bind(std::forward<F>(f), std::forward<Args>(args)...) ); std::future<RetType> future = task->get_future(); // 封装任务并添加到队列 add_task([task]() { (*task)(); }); return future; } /* 私有方法 */ private: // 从任务队列获取一个任务 Task get_task() { unique_lock<mutex> lck(mu); // unique_lock 与 lock_guard的区别是,unique_lock 可以随时加锁 解锁,在cv.wait()时加锁,唤醒后自动解锁 while (tasks_queue.empty() && !is_stop.load()) // 线程池未停止,但是任务队列为空 { cv.wait(lck); // 一直等到任务队列来任务 } if (is_stop.load()) // 线程池已经停止 return Task(); // 返回一个空的任务 assert(!tasks_queue.empty()); // 断言 任务队列不为空 Task task = move(tasks_queue.front()); // 从队首取出一个任务 tasks_queue.pop(); cv.notify_one(); // 唤醒一个进程来处理取出来的任务 return task; } // 添加任务到任务队列 void add_task(Task task) { // 此处需要加锁,queue不是线程安全的 lock_guard<mutex> lkc{ mu }; // lock_guard 构造函数中加锁,析构函数中解锁 tasks_queue.push(task); cv.notify_one(); } // 主调度函数,线程执行的函数 void scheduler() { while (!is_stop.load()) { Task task(get_task()); if (task) { idle_thread_num--; // 空闲线程数 -1 task(); idle_thread_num++; // 执行完任务,此线程又变为空闲 } } } }; }
main.cpp  测试函数

#include "ThreadPool.h" #include <chrono> int test1(int num) { this_thread::sleep_for(chrono::seconds(1)); cout << "test1: " << num << endl; return ((num == 0) ? 0 : num / 2); } int test2(int num1, int num2) { this_thread::sleep_for(chrono::seconds(1)); cout << "test2: " << num1 << " " << num2 << endl; return 1000; } int main() { ThreadPool::ThreadPool workers(3); // 创建一个有10个线程的线程池 // 输出开始状态: std::cout << "at the beginning: " << std::endl; std::cout << "idle threads: " << workers.get_idle_thread_num() << std::endl; std::cout << "tasks: " << workers.get_task_num() << std::endl; future<int>submit1 = workers.submit(test1, 10); // 提交一个任务 std::cout << "after 1 sunmit: " << std::endl; std::cout << "idle threads: " << workers.get_idle_thread_num() << std::endl; std::cout << "tasks: " << workers.get_task_num() << std::endl; future<int>submit2 = workers.submit(test1, 20); // 提交一个任务 std::cout << "after 2 sunmit: " << std::endl; std::cout << "idle threads: " << workers.get_idle_thread_num() << std::endl; std::cout << "tasks: " << workers.get_task_num() << std::endl; future<int>submit3 = workers.submit(bind(test2,1,2)); // 提交一个任务 std::cout << "after 3 sunmit: " << std::endl; std::cout << "idle threads: " << workers.get_idle_thread_num() << std::endl; std::cout << "tasks: " << workers.get_task_num() << std::endl; cout << submit1.get() << endl; cout << submit2.get() << endl; cout << submit3.get() << endl; std::cout << "end: " << std::endl; std::cout << "idle threads: " << workers.get_idle_thread_num() << std::endl; std::cout << "tasks: " << workers.get_task_num() << std::endl; return 0; }

 

 

面试题

1.题目:子线程循环 10 次,接着主线程循环 100 次,接着又回到子线程循环 10 次,接着再回到主线程又循环 100 次,如此循环50次,试写出代码

#include <iostream>             // std::cout
#include <mutex>
#include <thread>         // std::thread
#include <condition_variable>

using namespace std;

mutex mu;
int flag = 10;
condition_variable cv;
void f(int num)
{
    for (int i = 0; i < 50; i++)
    {
        unique_lock<mutex> lk(mu); 
        while (flag != num) // 这里不能用if,一定要用while
            cv.wait(lk); // 条件变量调用wait会自动调用锁
        for (int j = 0; j < num; ++j)
        {
            cout << j << " ";
        }
        cout << endl;
        flag = (num == 10) ? 100 : 10;
        cv.notify_one();
    }
}

int main()
{
    thread child(f, 10);
    f(100);
    child.join();
    return 0;
}

2.编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推

#include <iostream>             // std::cout
#include <mutex>
#include <thread>         // std::thread
#include <condition_variable>

using namespace std;

mutex mu;
int flag = 0;
condition_variable cv;
void f(int num)
{
    for (int i = 0; i < 10; i++)
    {
        unique_lock<mutex> lk(mu); 
        while (flag != num) // 这里不能用if 一定要用while
            cv.wait(lk); // 条件变量调用wait会自动调用锁
        char c = 'A' + flag;
        cout << c;
        flag = (flag+1)%3;
        cv.notify_all(); //唤醒其他进程
    }
}

int main()
{
    thread child1(f, 0);
    thread child2(f, 1);
    f(2);
    child1.join();
    child2.join();
    return 0;
}

3.题目(google笔试题):有四个线程1、2、3、4。线程1的功能就是输出1,线程2的功能就是输出2,以此类推.........现在有四个文件ABCD。初始都为空。现要让四个文件呈如下格式:

A:1 2 3 4 1 2....
B:2 3 4 1 2 3....
C:3 4 1 2 3 4....

D:4 1 2 3 4 1....

#include <iostream>             // std::cout
#include <mutex>
#include <thread>         // std::thread
#include <condition_variable>

using namespace std;

mutex mu;
int flag = 0; // 改变flag的值,就改变了1234的输出顺序
condition_variable cv;

void f(int num)
{
    for (int i = 0; i < 100; i++)
    {
        unique_lock<mutex> lk(mu); 
        while (flag != num)
            cv.wait(lk); // 条件变量调用wait会自动调用锁
        cout << num + 1 << " ";
        flag = (flag + 1) % 4;
        cv.notify_all(); //唤醒其他进程
    }
}

int main()
{
    thread child1(f, 0);
    thread child2(f, 1);
    thread child3(f, 2);
    f(3);

    child1.join();
    child2.join();
    child3.join();
    cout << endl;

    //system("pause");
    return 0;
}

4.读写者问题:这也是一个非常经典的多线程题目,题目大意如下:有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者去读文件,同样有读者读时写者也不能写

 

class rwlock {
private:
    mutex _lock;
    condition_variable _wcon, _rcon; // 写者和读者的条件变量
    unsigned _writer, _reader; // 分别表示写者和读者数量
    int _active; // -1:有写者正在写   0:没有读者也没有写者    大于0:有读者在读,且等于读者数量
public:
    void read_lock() {
        unique_lock<mutex> lock(_lock);
        ++_reader;
        while (_active < 0 || _writer > 0) // 在有写者存在时,读者挂起
            _rcon.wait(lock);
        --_reader;
        ++_active;
    }
    void write_lock() {
        unique_lock<mutex> lock(_lock);
        ++_writer;
        while (_active != 0) // 在有读者或者写者存在读或者在写时,写者挂起
            _wcon.wait(lock);
        --_writer;
        _active = -1;
    }
    void unlock() { // 主要是对 _active 变量进行恢复
        unique_lock<mutex> lock(_lock);
        if (_active > 0) { // 要解锁的是读者
            --_active;
            if (_active == 0) _wcon.notify_one();
        }
        else { // 要解锁的是写者
            _active = 0;
            if (_writer > 0) _wcon.notify_one(); // 此时若写者数量不为0(有其他写者在等待)
            else if (_reader > 0) _rcon.notify_all(); // 此时没有写者等待,只有读者等待,则唤醒所有读者
        }

    }
    rwlock() :_writer(0), _reader(0), _active(0) {
    }
};

void t1(rwlock* rwl) {
    while (1) {
        cout << "I want to write." << endl;
        rwl->write_lock();
        cout << "writing..." << endl;
        this_thread::sleep_for(chrono::seconds(5));
        rwl->unlock();
        this_thread::sleep_for(chrono::seconds(5));
    }
}

void t2(rwlock* rwl) {
    while (1) {
        cout << "t2-I want to read." << endl;
        rwl->read_lock();
        cout << "t2-reading..." << endl;
        this_thread::sleep_for(chrono::seconds(1));
        rwl->unlock();
    }
}

void t3(rwlock* rwl) {
    while (1) {
        cout << "t3-I want to read." << endl;
        rwl->read_lock();
        cout << "t3-reading..." << endl;
        this_thread::sleep_for(chrono::seconds(1));
        rwl->unlock();
    }
}

int main()
{
    rwlock* rwl = new rwlock();
    thread th1(t1, rwl);
    thread th2(t2, rwl);
    thread th3(t3, rwl);
    th1.join();
    th2.join();
    th3.join();

    system("pause");
    return 0;
}

 

原文链接: https://www.cnblogs.com/qiang-wei/p/12431377.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    C++多线程

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/333670

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月1日 下午9:17
下一篇 2023年3月1日 下午9:17

相关推荐