やりたいこと
今回それを使おうと思ったのだが、マルチスレッドな処理で使いたいので、排他処理を入れたい。
また、ちょっと書き方がぐちゃぐちゃなので、多少でもマシなコードにしたい。
実験コード
最近知った諸々を取り入れて、こんな感じにしてみた。
#pragma once #include <string> #include <filesystem> #include <mutex> class LogOnDesktop { private: std::mutex mtx; std::filesystem::path AppendLogFilePathOnDesktopString(std::wstring filename); std::wstring GetTimeString(); public: void WriteLine(std::wstring txt); };
#include <iostream> #include <stdio.h> #include <tchar.h> #include <string> #include <shlobj.h> #include <time.h> #include <thread> // std::this_thread::get_id()を使うのに必要 #include <fstream> // std::wofstreamを使うのに必要 #include <filesystem> #include <mutex> #include "LogOnDesktop.h" // ※このコードをブラウザからコピーしてVisualStudioに貼り付けると、ビルドしたときに // 文字コード云々でエラーになったことがあった。そういうときは、コード中のコメントを全部消すとエラー消えた。 std::filesystem::path LogOnDesktop::AppendLogFilePathOnDesktopString(std::wstring filename) { WCHAR* buf = nullptr; std::filesystem::path desktop; auto hr = SHGetKnownFolderPath(FOLDERID_Desktop, KF_FLAG_DEFAULT, NULL, &buf); if (SUCCEEDED(hr)) { desktop = buf; desktop.append(filename); } CoTaskMemFree(buf); return desktop; } std::wstring LogOnDesktop::GetTimeString() { WCHAR buf[64]; auto t = time(nullptr); auto tmv = tm(); auto error = localtime_s(&tmv, &t); // ローカル時間(タイムゾーンに合わせた時間)を取得 wcsftime(buf, 64, L"%Y/%m/%d %H:%M:%S", &tmv); auto aika = std::wstring(buf); return aika; } void LogOnDesktop::WriteLine(std::wstring txt) { std::lock_guard<std::mutex> lock(mtx); auto desktop = AppendLogFilePathOnDesktopString(L"mylog.log"); DWORD processId = GetCurrentProcessId(); auto aika = GetTimeString(); // 現在のスレッドIDを出力 auto thId = std::this_thread::get_id(); // ファイルを開く(なければ作成) std::wofstream ofs(desktop, std::ios::app); if (!ofs) return; // 現在時刻とスレッドIDを付けたログをファイルに書き込み ofs << aika << L" " << processId << L" " << thId << L" " << txt << std::endl; // ファイル閉じる ofs.close(); }
メインのcpp
#include "LogOnDesktop.h" int main() { LogOnDesktop log; for (size_t i = 0; i < 10; i++) { log.WriteLine(std::to_wstring(i)); } }
出力はこんな感じ。
2024/01/25 22:04:13 16000 0 2024/01/25 22:04:13 16000 1 2024/01/25 22:04:13 16000 2 2024/01/25 22:04:13 16000 3 2024/01/25 22:04:13 16000 4 2024/01/25 22:04:13 16000 5 2024/01/25 22:04:13 16000 6 2024/01/25 22:04:13 16000 7 2024/01/25 22:04:13 16000 8 2024/01/25 22:04:13 16000 9
マルチスレッドでも大丈夫なことの確認
下記のように、main関数を変えて試した。
int main() { static std::thread th_a; static std::thread th_b; LogOnDesktop log; th_a = std::thread([&log] { for (size_t i = 0; i < 10; i++) { log.WriteLine(std::to_wstring(i)); } }); th_b = std::thread([&log] { for (size_t i = 0; i < 10; i++) { log.WriteLine(std::to_wstring(i*100)); } }); // プログラム終了時にはjoinで終わるのを待つ th_a.join(); th_b.join(); }
出力
2024/01/25 22:24:07 10844 0 2024/01/25 22:24:07 10844 100 2024/01/25 22:24:07 16516 0 2024/01/25 22:24:07 10844 200 2024/01/25 22:24:07 10844 300 2024/01/25 22:24:07 10844 400 2024/01/25 22:24:07 10844 500 2024/01/25 22:24:07 10844 600 2024/01/25 22:24:07 10844 700 2024/01/25 22:24:07 10844 800 2024/01/25 22:24:07 10844 900 2024/01/25 22:24:07 16516 1 2024/01/25 22:24:07 16516 2 2024/01/25 22:24:07 16516 3 2024/01/25 22:24:07 16516 4 2024/01/25 22:24:07 16516 5 2024/01/25 22:24:07 16516 6 2024/01/25 22:24:07 16516 7 2024/01/25 22:24:07 16516 8 2024/01/25 22:24:07 16516 9
スレッドAとBで、変に、1件のログの間に別のログが挟まったりすることなく、出力できてる。
※スレッドAで10件、Bで10件と連続して出ないのはOK。
参考
[C++] 特殊フォルダのパスを取得する
https://qiita.com/tera1707/private/9d79b31875c5d275c67f
std::filesystem::path::append
https://cpprefjp.github.io/reference/filesystem/path/append.html
ミューテックス(std::mutex)でlockする②