我目前正在学习c ++ 11中引入的多线程功能,当我正在筛选几个SO问题时,我发现多线程在执行指令的同时带来了自己的开销。所以我写了一个简单的程序来比较b / w顺序和多线程解决方案。结论并没有让我感到惊讶,顺序比多线程更快,可能是因为它没有必要处理线程的创建和管理。
但是现在对于另一个问题,顺序方法会阻塞整个程序,多线程可能会有一个优势 -
input.txt
DEVICE#1 4
DEVICE#2 5
DEVICE#3 10
DEVICE#4 1
DEVICE#1 1
prog_seq.cpp
#include <chrono>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
using string = std::string;
using namespace std::this_thread; // sleep_for
void executor(string name, int delay)
{
sleep_for(std::chrono::seconds(delay));
std::cout << name << std::endl;
}
int main()
{
string deviceName = "";
int delay = 0;
std::ifstream in("input.txt");
while (in >> deviceName >> delay) {
executor(deviceName, delay);
}
return 0;
}
Output
DEVICE#1
DEVICE#2
DEVICE#3
DEVICE#4
DEVICE#1
real 0m21.004s
user 0m0.000s
sys 0m0.000s
这个程序至少需要21秒才能完成,而下一个程序可能会在11secs下完成 -
prog_thread.cpp
#include <chrono>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
using string = std::string;
using namespace std::this_thread; // sleep_for
void executor(string name, int delay)
{
sleep_for(std::chrono::seconds(delay));
std::cout << name << std::endl;
}
int main()
{
std::vector<std::thread> threadStore;
string deviceName = "";
int delay = 0;
std::ifstream in("input.txt");
while (in >> deviceName >> delay) {
threadStore.emplace_back(std::thread(executor, deviceName, delay));
}
for (auto &t : threadStore) {
t.join();
}
return 0;
}
Output
DEVICE#1
DEVICE#4
DEVICE#1
DEVICE#2
DEVICE#3
real 0m10.003s
user 0m0.000s
sys 0m0.000s
所以我理解,对于这样的程序,多线程实际上会受益,因为它们以最短的顺序完成,因为它们以良好的方式切换(如果它们的数量增长高于核心)。 / p>
现在我想问的是,对于这样的程序,每个线程执行相互独立的任务,而不是完全 cpu密集,而更多地依赖于等待来自外部的输入或响应系统/人类,多线程的开销是否仍适用于此?假设我有50个线程都在等待来自50个客户端的输入,并且因为他们不忙于执行cpu密集型任务,这对于顺序执行中的某种计时器来说是否是更好的解决方案(在一次又一次300msecs后继续检查输入。)
此外,是否还有其他方法可以解决这些问题,因为我很想知道这些问题。
答案 0 :(得分:1)
正如评论中所指出的,线程管理是Comp Sci中一个非常广泛且研究得很好的主题。对于您的网站情况,我可以举几个行业示例,您可能会发现这些示例。
通常,从程序员的角度来看,拥有许多I / O绑定的线程是一个简单且可维护的解决方案。每个套接字连接启动一个线程是一个常见的例子。只要线程随着时间的推移而进行大量工作,线程设置/拆除就无关紧要了。通常,Linux调度程序可以很好地为所有线程提供服务。当线程可以在诸如套接字读取之类的阻塞资源上等待时,此模型特别好。如果所有线程都非常繁忙,那么模型就不好了,上下文切换花费的时间也很长。根据延迟窗口,一些线程也可能会超时,因为抢占很常见。
在频谱的另一端,Reactor模型可以与一小组线程一起使用。在此模型中,启动每个CPU核心的线程。线程运行“热”并且不允许阻塞。许多客户可以循环或类似方式在单个Reactor中提供服务。可以启动多个独立的Reactor来扩展系统。如果所有客户端都很少忙I / O,那么这个模型很不错。 Reactor模型最适合对延迟敏感的应用程序。一个特定的工作将被Reactor接受并运行完成而没有先发制人。
Node.js引擎使用与Reactor类似的模型,称为Observer。这两种模式相似但不完全是same。
Reactor / Observer模型的缺点是作业处理的异步性质。许多业务应用程序具有自然阻塞或循环点。必须将算法重构为异步通常会造成混淆并影响可维护性。对节点文献的简要调查将揭示对异步算法的痴迷。线程模式是一个关键原因。
线程管理与排队论密切相关。业务应用程序的最佳线程模型通常表示传入作业的排队模型。 Queuing Theory是一个复杂而有趣的领域。
总之,对于大规模,高吞吐量和/或低延迟的应用程序,线程模型是关键架构决策。要成功解决问题,需要彻底了解业务问题和基础计算机科学。请在我的blog上查看有关此主题的更多信息。