• 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;
    }
  • 相关阅读:
    Windows Phone 独立存储资源管理器工具
    Windows Phone 选择器
    Windows Phone 启动器
    Windows Phone 8 ControlTiltEffect
    ActivatedEventArgs.IsApplicationInstancePreserved 属性
    HttpWebRequest BeginGetResponse EndGetResponse
    python并发编程-进程间通信-Queue队列使用-生产者消费者模型-线程理论-创建及对象属性方法-线程互斥锁-守护线程-02
    python并发编程-进程理论-进程方法-守护进程-互斥锁-01
    python网络编程-socket套接字通信循环-粘包问题-struct模块-02
    python网络编程-异常处理-异常捕获-抛出异常-断言-自定义异常-UDP通信-socketserver模块应用-03
  • 原文地址:https://www.cnblogs.com/qiang-wei/p/12431377.html
Copyright © 2020-2023  润新知