From b50ceec101dde9d91e851943c8debdb53c895486 Mon Sep 17 00:00:00 2001 From: daohwang <1760730301@qq.com> Date: Wed, 2 Feb 2022 14:03:26 +0800 Subject: [PATCH 1/2] finish hw05 --- CMakeLists.txt | 2 + MTQueue.h | 10 ++- main.cpp | 168 +++++++++++++++++++++++++++++++++---------------- 3 files changed, 126 insertions(+), 54 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c555708..b321986 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,3 +10,5 @@ add_executable(main main.cpp) find_package(Threads REQUIRED) target_link_libraries(main PUBLIC Threads::Threads) + +target_include_directories(main PRIVATE .) diff --git a/MTQueue.h b/MTQueue.h index 38d2eee..1142cbe 100644 --- a/MTQueue.h +++ b/MTQueue.h @@ -7,10 +7,16 @@ template class MTQueue { std::condition_variable m_cv; - std::mutex m_mtx; + mutable std::mutex m_mtx; std::vector m_arr; public: + + size_t size() const { + std::lock_guard lk(m_mtx); + return m_arr.size(); + } + T pop() { std::unique_lock lck(m_mtx); m_cv.wait(lck, [this] { return !m_arr.empty(); }); @@ -43,6 +49,7 @@ class MTQueue { } }; +#ifdef MTQueue_TEST int main() { MTQueue foods; @@ -69,3 +76,4 @@ int main() { return 0; } +#endif diff --git a/main.cpp b/main.cpp index f4ecab8..e3b76c3 100644 --- a/main.cpp +++ b/main.cpp @@ -1,89 +1,151 @@ // 小彭老师作业05:假装是多线程 HTTP 服务器 - 富连网大厂面试官觉得很赞 +#include +#include +#include #include #include +#include +#include +#include +#include #include -#include #include #include -#include - struct User { - std::string password; - std::string school; - std::string phone; + std::string password; + std::string school; + std::string phone; }; +std::shared_mutex lk_users; +std::shared_mutex lk_login; std::map users; std::map has_login; // 换成 std::chrono::seconds 之类的 // 作业要求1:把这些函数变成多线程安全的 // 提示:能正确利用 shared_mutex 加分,用 lock_guard 系列加分 -std::string do_register(std::string username, std::string password, std::string school, std::string phone) { - User user = {password, school, phone}; - if (users.emplace(username, user).second) - return "注册成功"; - else - return "用户名已被注册"; +std::string do_register(std::string username, std::string password, + std::string school, std::string phone) { + User user = {password, school, phone}; + std::unique_lock lock(lk_users); + if (users.emplace(username, user).second) + return "注册成功"; + else + return "用户名已被注册"; } std::string do_login(std::string username, std::string password) { - // 作业要求2:把这个登录计时器改成基于 chrono 的 - long now = time(NULL); // C 语言当前时间 - if (has_login.find(username) != has_login.end()) { - int sec = now - has_login.at(username); // C 语言算时间差 - return std::to_string(sec) + "秒内登录过"; - } - has_login[username] = now; + // 作业要求2:把这个登录计时器改成基于 chrono 的 + long now = time(NULL); // C 语言当前时间 + std::unique_lock lk(lk_login); + if (has_login.find(username) != has_login.end()) { + int sec = now - has_login.at(username); // C 语言算时间差 + return std::to_string(sec) + "秒内登录过"; + } + has_login[username] = now; + lk.unlock(); - if (users.find(username) == users.end()) - return "用户名错误"; - if (users.at(username).password != password) - return "密码错误"; - return "登录成功"; + std::shared_lock lk_u(lk_users); + if (users.find(username) == users.end()) return "用户名错误"; + if (users.at(username).password != password) return "密码错误"; + return "登录成功"; } std::string do_queryuser(std::string username) { - auto &user = users.at(username); - std::stringstream ss; - ss << "用户名: " << username << std::endl; - ss << "学校:" << user.school << std::endl; - ss << "电话: " << user.phone << std::endl; - return ss.str(); + std::shared_lock lk(lk_users); + if (users.find(username) == users.end()) { + return "用户名错误"; + } + auto& user = users.at(username); + std::stringstream ss; + ss << "用户名: " << username << std::endl; + ss << "学校:" << user.school << std::endl; + ss << "电话: " << user.phone << std::endl; + return ss.str(); } - -struct ThreadPool { - void create(std::function start) { - // 作业要求3:如何让这个线程保持在后台执行不要退出? - // 提示:改成 async 和 future 且用法正确也可以加分 - std::thread thr(start); +class ThreadPool { + public: + explicit ThreadPool(size_t num_workers) : stop_(false) { + std::cout << "worker number: " << num_workers << std::endl; + workers_.reserve(num_workers); + for (size_t i = 0; i < num_workers; ++i) { + workers_.push_back(std::thread([&]() -> void { + while (true) { + std::unique_lock lk(mtx_tasks_); + cv_tasks_.wait(lk, [&]() { return stop_ || !tasks_.empty(); }); + if (stop_) { + break; + } + auto task = std::move(tasks_.front()); + tasks_.pop(); + task(); + } + })); } -}; + } -ThreadPool tpool; + ThreadPool(const ThreadPool& rhs) = delete; + ThreadPool& operator=(const ThreadPool& rhs) = delete; + ThreadPool(ThreadPool&& rhs) = default; + ThreadPool& operator=(ThreadPool&& rhs) = default; + ~ThreadPool() { + stop_ = true; + cv_tasks_.notify_all(); + for (auto& worker : workers_) worker.join(); + std::cout << "ThreadPool finished!" << std::endl; + } + + void create(std::function start) { + // 作业要求3:如何让这个线程保持在后台执行不要退出? + // 提示:改成 async 和 future 且用法正确也可以加分 + std::lock_guard lk(mtx_tasks_); + tasks_.push(std::move(start)); + cv_tasks_.notify_one(); + } + + private: + using task = std::function; + std::queue tasks_; + std::vector workers_; + std::mutex mtx_tasks_; + std::condition_variable cv_tasks_; + bool stop_; +}; namespace test { // 测试用例?出水用力! std::string username[] = {"张心欣", "王鑫磊", "彭于斌", "胡原名"}; std::string password[] = {"hellojob", "anti-job42", "cihou233", "reCihou_!"}; -std::string school[] = {"九百八十五大鞋", "浙江大鞋", "剑桥大鞋", "麻绳理工鞋院"}; +std::string school[] = {"九百八十五大鞋", "浙江大鞋", "剑桥大鞋", + "麻绳理工鞋院"}; std::string phone[] = {"110", "119", "120", "12315"}; -} +} // namespace test int main() { - for (int i = 0; i < 262144; i++) { - tpool.create([&] { - std::cout << do_register(test::username[rand() % 4], test::password[rand() % 4], test::school[rand() % 4], test::phone[rand() % 4]) << std::endl; - }); - tpool.create([&] { - std::cout << do_login(test::username[rand() % 4], test::password[rand() % 4]) << std::endl; - }); - tpool.create([&] { - std::cout << do_queryuser(test::username[rand() % 4]) << std::endl; - }); - } + constexpr int M = 262144; + // constexpr int M = 10000; + + ThreadPool tpool(std::thread::hardware_concurrency()); + + for (int i = 0; i < M; i++) { + tpool.create([&] { + std::cout << do_register( + test::username[rand() % 4], test::password[rand() % 4], + test::school[rand() % 4], test::phone[rand() % 4]) + << std::endl; + }); + tpool.create([&] { + std::cout << do_login(test::username[rand() % 4], + test::password[rand() % 4]) + << std::endl; + }); + tpool.create([&] { + std::cout << do_queryuser(test::username[rand() % 4]) << std::endl; + }); + } - // 作业要求4:等待 tpool 中所有线程都结束后再退出 - return 0; + // 作业要求4:等待 tpool 中所有线程都结束后再退出 + return 0; } From 90259643f124ffa05b2d343a6a43f5ac309a0105 Mon Sep 17 00:00:00 2001 From: daohwang <1760730301@qq.com> Date: Wed, 2 Feb 2022 20:42:26 +0800 Subject: [PATCH 2/2] finish hw05 --- main.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/main.cpp b/main.cpp index e3b76c3..4e2b2a6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,6 @@ // 小彭老师作业05:假装是多线程 HTTP 服务器 - 富连网大厂面试官觉得很赞 -#include +#include +#include #include #include #include @@ -21,7 +22,8 @@ struct User { std::shared_mutex lk_users; std::shared_mutex lk_login; std::map users; -std::map has_login; // 换成 std::chrono::seconds 之类的 +std::map + has_login; // 换成 std::chrono::seconds 之类的 // 作业要求1:把这些函数变成多线程安全的 // 提示:能正确利用 shared_mutex 加分,用 lock_guard 系列加分 @@ -37,10 +39,11 @@ std::string do_register(std::string username, std::string password, std::string do_login(std::string username, std::string password) { // 作业要求2:把这个登录计时器改成基于 chrono 的 - long now = time(NULL); // C 语言当前时间 + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); std::unique_lock lk(lk_login); if (has_login.find(username) != has_login.end()) { - int sec = now - has_login.at(username); // C 语言算时间差 + auto tt = now - has_login.at(username); + int64_t sec = std::chrono::duration_cast(tt).count(); return std::to_string(sec) + "秒内登录过"; } has_login[username] = now; @@ -67,7 +70,7 @@ std::string do_queryuser(std::string username) { class ThreadPool { public: - explicit ThreadPool(size_t num_workers) : stop_(false) { + explicit ThreadPool(size_t num_workers) : stop_(false), counter_(0) { std::cout << "worker number: " << num_workers << std::endl; workers_.reserve(num_workers); for (size_t i = 0; i < num_workers; ++i) { @@ -75,12 +78,13 @@ class ThreadPool { while (true) { std::unique_lock lk(mtx_tasks_); cv_tasks_.wait(lk, [&]() { return stop_ || !tasks_.empty(); }); - if (stop_) { + if (stop_ && tasks_.empty()) { break; } auto task = std::move(tasks_.front()); tasks_.pop(); task(); + counter_++; } })); } @@ -96,6 +100,7 @@ class ThreadPool { cv_tasks_.notify_all(); for (auto& worker : workers_) worker.join(); std::cout << "ThreadPool finished!" << std::endl; + std::cout << counter_ << " tasks done!" << std::endl; } void create(std::function start) { @@ -113,6 +118,7 @@ class ThreadPool { std::mutex mtx_tasks_; std::condition_variable cv_tasks_; bool stop_; + std::atomic counter_; }; namespace test { // 测试用例?出水用力! @@ -124,8 +130,8 @@ std::string phone[] = {"110", "119", "120", "12315"}; } // namespace test int main() { - constexpr int M = 262144; - // constexpr int M = 10000; + // constexpr int M = 262144; + constexpr int M = 10000; ThreadPool tpool(std::thread::hardware_concurrency());