异步打印日志的优点
日志服务是一个常见的后台服务,它通常需要大量的日志输出来记录各种操作和事件。在服务的运行过程中,频繁地输出日志信息会带来一定的性能开销,降低服务的吞吐能力和响应速度,甚至可能导致服务崩溃。
因此,异步打印日志可以解决这个问题。异步打印日志的基本思路是:将要打印的日志信息先放入一个缓存队列中,由另一个线程去读取队列中的日志信息,并将其写入到日志文件中。这样就可以将日志输出的过程异步化,避免了频繁的文件操作对服务性能的影响。
具体来说,异步打印日志的优点包括:
- 1.减少对主线程的干扰:在主线程中直接打印日志会降低服务的响应速度,而异步打印日志可以将日志输出的过程与主线程分离,不会对主线程造成太大的干扰。
- 2.提高服务的吞吐能力:异步打印日志可以将日志输出的过程与主线程分离,这样可以避免频繁的文件操作对服务性能的影响,从而提高服务的吞吐能力。
- 3.增加程序的健壮性:异步打印日志可以将日志输出的过程与主线程分离,这样即使在打印日志的过程中出现异常,也不会影响主线程的运行,从而增加程序的健壮性。
总之,异步打印日志可以提高服务的性能和稳定性,是一个非常值得采用的优化方案。
代码示例
#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
class AsyncLogger {
public:
// 构造函数,初始化日志文件名和队列最大长度,并打开日志文件流
AsyncLogger(const std::string& log_filename, size_t max_queue_size = 10000) :
m_max_queue_size(max_queue_size),
m_log_stream(log_filename, std::ios::app)
{
// 启动工作线程
m_worker_thread = std::thread(&AsyncLogger::worker_func, this);
}
// 析构函数,结束工作线程
~AsyncLogger() {
// 设置 m_done 标志,通知工作线程退出
m_done = true;
// 通知所有等待线程,工作线程结束
m_cv.notify_all();
// 等待工作线程退出
m_worker_thread.join();
}
// 日志函数,将日志信息添加到队列中
void log(const std::string& message) {
std::unique_lock<std::mutex> lock(m_mutex);
// 如果队列已满,则等待队列有空位
while (m_log_queue.size() >= m_max_queue_size) {
m_cv.wait(lock);
}
// 添加日志信息到队列中
m_log_queue.push(message);
// 通知工作线程有新的日志信息
m_cv.notify_one();
}
private:
// 工作线程函数,不断从队列中取出日志信息并写入日志文件中
void worker_func() {
while (!m_done) {
std::unique_lock<std::mutex> lock(m_mutex);
// 如果队列为空,则等待新的日志信息
while (m_log_queue.empty() && !m_done) {
m_cv.wait(lock);
}
// 处理队列中的所有日志信息
while (!m_log_queue.empty()) {
const auto& message = m_log_queue.front();
m_log_stream << message << std::endl;
m_log_queue.pop();
}
// 通知所有等待线程,队列中有空位了
m_cv.notify_all();
}
}
std::queue<std::string> m_log_queue; // 日志信息队列
const size_t m_max_queue_size; // 队列最大长度
std::ofstream m_log_stream; // 日志文件流
std::mutex m_mutex; // 互斥锁
std::condition_variable m_cv; // 条件变量,用于线程同步
std::thread m_worker_thread; // 工作线程
bool m_done = false; // 工作线程结束标志
};
int main() {
AsyncLogger logger("test.log");
// 向日志队列中添加 10000 条日志信息
for (int i = 0; i < 10000; ++i) {
logger.log("log message " + std::to_string(i));
}
return 0;
}
运行结果截图
windows编程代码
#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
const int MAX_LOG_QUEUE_SIZE = 1000;
// 日志消息结构体
struct LogMessage {
std::string message;
int level;
};
// 日志队列类
class LogQueue {
public:
LogQueue() : m_stop(false) {}
// 添加日志消息到队列
void push(const LogMessage& message) {
std::unique_lock<std::mutex> lock(m_mutex);
m_queue.push(message);
m_cond.notify_one();
}
// 停止日志队列处理
void stop() {
std::unique_lock<std::mutex> lock(m_mutex);
m_stop = true;
m_cond.notify_all();
}
// 从日志队列中弹出一条消息,如果队列为空,则等待
bool try_pop(LogMessage& message) {
std::unique_lock<std::mutex> lock(m_mutex);
while (m_queue.empty() && !m_stop) {
m_cond.wait(lock);
}
if (m_queue.empty()) {
return false;
}
message = m_queue.front();
m_queue.pop();
return true;
}
private:
std::queue<LogMessage> m_queue;
std::mutex m_mutex;
std::condition_variable m_cond;
bool m_stop;
};
// 日志类
class Logger {
public:
Logger(const std::string& logFile) : m_logFile(logFile), m_logLevel(0), m_queueThread(&Logger::processQueue, this) {}
// 析构函数,停止日志队列处理并等待线程结束
~Logger() {
m_queue.stop();
m_queueThread.join();
}
// 设置日志级别
void setLogLevel(int level) {
m_logLevel = level;
}
// 添加一条日志消息到队列
void log(int level, const std::string& message) {
if (level >= m_logLevel) {
LogMessage tmp;
tmp.level = level;
tmp.message = message;
m_queue.push(tmp);
}
}
private:
// 处理日志队列中的消息,将其写入日志文件
void processQueue() {
std::ofstream logStream(m_logFile);
if (!logStream.is_open()) {
std::cerr << "Error opening log file " << m_logFile << std::endl;
return;
}
while (true) {
LogMessage message;
if (!m_queue.try_pop(message)) {
if (m_queueStopped) {
break;
} else {
continue;
}
}
logStream << "[" << message.level << "] " << message.message << std::endl;
}
logStream.close();
}
std::string m_logFile; // 日志文件名
int m_logLevel; // 日志级别
LogQueue m_queue; // 日志消息队列
bool m_queueStopped; // 日志队列是否停止
std::thread m_queueThread; // 日志队列处理线程
};
int main() {
Logger logger("log.txt");
logger.setLogLevel(1);
logger.log(0, "This message should not be logged");
logger.log(1, "This message should be logged with level 1");
return 0;
}
运行结果
log.txt
[1] This message should be logged with level 1