C++线程安全的队列你了解嘛

C++线程安全的队列

什么是线程安全的队列?

线程安全的队列是可以在多个线程同时读写时保证数据一致性和正确性的队列。在多个线程同时对同一个队列进行读写操作时,若不进行同步控制,就会出现数据异常和不一致的情况。线程安全的队列就是为了解决这个问题而设计的一种数据结构。

如何设计线程安全的队列?

设计线程安全的队列主要需要解决以下两个问题:

  1. 如何对队列进行同步控制:对队列的读写操作必须进行同步控制,保证线程之间的数据一致性和正确性。
  2. 如何实现队列的性能优化:一个高效的队列需要具备一定的性能优化,比如延迟申请内存、预申请内存空间等。

一般而言,可以使用互斥锁或者自旋锁来对队列进行同步控制。具体实现细节可参考以下示例代码:

#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <chrono>

template<typename T>
class SafeQueue
{
private:
    std::queue<T> _queue;               // 队列
    std::mutex _mutex;                  // 互斥锁

public:
    bool empty() { std::lock_guard<std::mutex> lock(_mutex); return _queue.empty(); }         // 判断队列是否为空
    int size() { std::lock_guard<std::mutex> lock(_mutex); return _queue.size(); }            // 获取队列的大小
    void push(T value) { std::lock_guard<std::mutex> lock(_mutex); _queue.push(value); }      // 将元素入队列
    T pop() { std::lock_guard<std::mutex> lock(_mutex); T value = _queue.front(); _queue.pop(); return value; }                 // 将元素出队列
};

// 测试
int main()
{
    SafeQueue<int> q;

    // 生产者
    std::thread producer([&]() {
        for (int i = 1; i <= 10; i++) {
            q.push(i);
            std::this_thread::sleep_for(std::chrono::milliseconds(10));  // 休眠10毫秒
        }
    });

    // 消费者
    std::thread consumer([&]() {
        while (true) {
            if (q.empty()) continue;

            int value = q.pop();
            std::cout << "消费者消费: " << value << std::endl;

            if (value == 10) break;         // 生产者线程结束循环后,退出消费者线程
        }
    });

    producer.join();
    consumer.join();

    return 0;
}

线程安全的队列的应用示例

应用示例1:多线程文件IO操作

多线程支持高效的文件IO操作是一个非常常见的应用场景,通常在一个线程中读取文件数据,然后将读取到的数据写入到线程安全的队列中,之后再在另外的线程中从队列中取出数据进行处理。以下是一个简单的多线程文件IO操作示例:

#include <iostream>
#include <fstream>
#include <thread>
#include <mutex>
#include <condition_variable>

template<typename T>
class SafeQueue
{
private:
    std::queue<T> _queue;                                   // 队列
    mutable std::mutex _mutex;                              // 互斥锁
    std::condition_variable _cond;                          // 条件变量

public:
    bool empty() const { std::lock_guard<std::mutex> lock(_mutex); return _queue.empty(); }         // 判断队列是否为空
    int size() const { std::lock_guard<std::mutex> lock(_mutex); return _queue.size(); }            // 获取队列的大小
    void push(T value) { std::lock_guard<std::mutex> lock(_mutex); _queue.push(value); _cond.notify_all(); }      // 将元素入队列
    T pop() { std::unique_lock<std::mutex> lock(_mutex); _cond.wait(lock, [=] { return !_queue.empty(); }); T value = _queue.front(); _queue.pop(); return value; }           // 将元素出队列并等待队列非空通知
};

void readFile(std::ifstream& ifs, SafeQueue<std::string>& q)
{
    std::string line;

    while (std::getline(ifs, line)) {
        q.push(line);
    }
}

void writeFile(std::ofstream& ofs, SafeQueue<std::string>& q)
{
    while (true) {
        if (q.empty()) continue;

        std::string line = q.pop();
        ofs << line << std::endl;

        if (line == "exit") break;
    }
}

int main()
{
    std::ifstream ifs("input.txt");
    std::ofstream ofs("output.txt");

    SafeQueue<std::string> q;

    // 读取文件
    std::thread reader([&]() { readFile(ifs, q); });

    // 写入文件
    std::thread writer([&]() { writeFile(ofs, q); });

    reader.join();
    writer.join();

    return 0;
}

应用示例2:多线程爬虫

多线程爬虫是另一个常见的应用场景,可以使用线程安全的队列来存储待爬取的URL,然后多个爬虫线程从队列中取出URL进行解析和爬取。以下是一个简单的多线程爬虫示例:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <curl/curl.h>

template<typename T>
class SafeQueue
{
private:
    std::queue<T> _queue;                                   // 队列
    mutable std::mutex _mutex;                              // 互斥锁
    std::condition_variable _cond;                          // 条件变量

public:
    bool empty() const { std::lock_guard<std::mutex> lock(_mutex); return _queue.empty(); }         // 判断队列是否为空
    int size() const { std::lock_guard<std::mutex> lock(_mutex); return _queue.size(); }            // 获取队列的大小
    void push(T value) { std::lock_guard<std::mutex> lock(_mutex); _queue.push(value); _cond.notify_all(); }      // 将元素入队列
    T pop() { std::unique_lock<std::mutex> lock(_mutex); _cond.wait(lock, [=] { return !_queue.empty(); }); T value = _queue.front(); _queue.pop(); return value; }           // 将元素出队列并等待队列非空通知
};

size_t writeFunction(void* ptr, size_t size, size_t nmemb, std::string* data)
{
    data->append((char*)ptr, size * nmemb);

    return size * nmemb;
}

void worker(SafeQueue<std::string>& q)
{
    CURL* curl = curl_easy_init();
    std::string url;

    while (true) {
        if (q.empty()) continue;

        url = q.pop();

        if (url == "exit") break;

        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

        std::string response;
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

        CURLcode res = curl_easy_perform(curl);

        if (res == CURLE_OK) {
            std::cout << url << ": " << response.size() << std::endl;
        } else {
            std::cerr << "HTTP error code: " << res << std::endl;
        }
    }

    curl_easy_cleanup(curl);
}

int main()
{
    SafeQueue<std::string> q;
    std::vector<std::thread> threads;

    threads.push_back(std::thread([&]() { worker(q); }));
    threads.push_back(std::thread([&]() { worker(q); }));
    threads.push_back(std::thread([&]() { worker(q); }));

    q.push("https://www.google.com/");
    q.push("https://www.baidu.com/");
    q.push("https://www.bing.com/");
    q.push("exit");

    for (auto& thread : threads) {
        thread.join();
    }

    return 0;
}

以上是线程安全队列的基本知识点和应用实例,希望对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++线程安全的队列你了解嘛 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • iOS读写json文件的方法示例

    在这里我将为你展示“iOS读写json文件的方法示例”的完整攻略,包括基本概念、操作步骤、示例代码和执行结果等方面的内容。 基本概念 在开始讲述攻略之前,我们需要了解一些基本概念: JSON:是一种轻量级的数据交换格式,具有可读性、易于解析和生成等特点。 JSON文件:是以JSON格式编写的文件,通常用于数据存储和传输。 操作步骤 想要在iOS中实现读写JS…

    C 2023年5月23日
    00
  • 在 .NET Framework 2.0 中未处理的异常导致基于 ASP.NET 的应用程序意外退出

    在 .NET Framework 2.0 中,未处理的异常可能会导致基于 ASP.NET 的应用程序意外退出。以下是完整攻略: 问题描述 基于 ASP.NET 的应用程序在处理某些异常情况时,未正确处理异常,导致应用程序发生了未预期的异常。这个问题往往会导致应用程序意外退出。原因可能是编码错误、内存不足或其他问题。 解决方案 要解决这个问题,需要进行以下步骤…

    C 2023年5月22日
    00
  • QT5连接MySQL实现增删改查

    下面就是QT5连接MySQL实现增删改查的完整攻略。 1. 安装MySQL驱动 在QT5中连接MySQL必须要安装MySQL驱动,你可以从以下链接中下载:https://www.mysql.com/products/connector/ 将下载好的驱动放在QT5安装目录下的plugins/sqldrivers目录下。 2. 配置项目文件 在.pro文件中添加…

    C 2023年5月23日
    00
  • java中Class.forName的作用浅谈

    《Java中Class.forName的作用浅谈》 简介 在Java中,如果要使用一个类,通常需要先加载该类。类的加载可以分为三个阶段:加载、链接和初始化。其中,加载阶段包括找到对应的字节码文件,并把该字节码文件读入到内存中;链接阶段包括验证、准备和解析的过程;初始化阶段是类的静态变量和静态代码块首次被执行的阶段。 在某些情况下,我们需要在运行时动态地加载某…

    C 2023年5月22日
    00
  • C语言中数据如何存储进内存揭秘

    C语言中数据如何存储进内存揭秘 C语言程序在运行时,需要使用计算机的内存来存储数据。C语言中的数据类型包括整型、浮点型、字符型等等,这些数据类型都有不同的内存分配方式,下面我们就来一一了解。 整型数据内存分配 对于整型数据,C语言会根据数据类型的位数分配不同字节的内存空间。例如,对于int类型的数据,一般分配4个字节的内存空间。这个内存空间会从计算机的内存地…

    C 2023年5月23日
    00
  • js实现div模拟模态对话框展现URL内容

    实现DIV模拟模态对话框展现URL内容的过程需要以下几个步骤: 创建一个DIV模拟对话框的框架,包括头部标题和关闭按钮。在这个DIV中,使用一个名为“content”的子DIV作为展示内容的容器。 使用JavaScript编写代码来获取指定URL的内容,并将内容插入到“content”子DIV中。可以使用AJAX技术获取URL内容。 将DIV模拟对话框显示在…

    C 2023年5月23日
    00
  • 如何用C语言添加矩阵

    添加矩阵是C语言中常见的任务之一。以下是一些基本的步骤: 1. 定义矩阵 在C语言中,可以使用二维数组来定义矩阵。例如,以下代码定义了一个3×3的矩阵: int matrix[3][3] = { {1,2,3}, {4,5,6}, {7,8,9} }; 2. 显示矩阵 可以使用循环来遍历矩阵中的所有元素,并将它们打印出来。例如,以下代码使用嵌套循环来遍历矩阵…

    C 2023年5月9日
    00
  • C语言利用system调用系统命令行详情

    C语言可以利用system()函数来调用系统命令行。一般情况下,system()函数会启动一个新的进程来执行传入的命令,然后等待该进程结束后返回。在使用system()函数时,需要先包含头文件<stdlib.h>。 函数原型 int system(const char *command); 参数说明 command:需要执行的命令字符串,可以是系…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部