我在我的 Mac 上运行 2 个相似的代码示例,一个是 C++,另一个是 C#。 2 个并行执行的简单任务(或者至少我希望它们这样做),一个在循环中打印“+”,另一个在循环中打印“-”。我原以为 2 个样本的输出非常相似,但出乎我的意料,它们有很大的不同。
C++ 似乎真正并行地运行任务。我可以在每次运行时看到 +- 很好地交替,但 C# 似乎运行一个任务一段时间,然后切换到另一个任务并运行一段时间。像这样:
C++: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
C# : ++++++++++---------++++++------
我知道不能对并行线程的运行方式做出假设,我很好奇 C++ 始终如一地产生如此好的结果。
感谢您的宝贵时间!
C#:
using System;
using System.Threading.Tasks;
public class ConcurrentTasks
{
public static void Main(String[] args)
{
var task1 = Task.Run(()=>DemoTask("+"));
var task2 = Task.Run(()=>DemoTask("-"));
var res1 = task1.Result;
var res2 = task2.Result;
Console.WriteLine("\nResults:");
Console.WriteLine(res1);
Console.WriteLine(res2);
}
private static String DemoTask(String label)
{
for (int i = 0; i < 1000; i++)
{
Console.Write(label);
}
return label + " result";
}
}
// mcs ConcurrentTasks.cs
// mono ConcurrentTasks.exe
C++:
#include <iostream>
#include <sstream>
#include <future>
using namespace std;
string demoTask(char label)
{
for (int i = 0; i < 1000; i++)
{
cout << label;
}
stringstream ss;
ss << label;
ss << " result";
return ss.str();
}
int main()
{
auto task1 = async(demoTask, '+');
auto task2 = async(demoTask, '-');
auto res1 = task1.get();
auto res2 = task2.get();
cout << endl << "Results:" << endl;
cout << res1 << endl;
cout << res2 << endl;
return 0;
}
// g++ --std=c++14 -Wall ConcurrentTasks.cpp -o ConcurrentTasks.exe
// ./ConcurrentTasks.exe
编辑:我已经将 C# 示例更改为使用裸线程,结果是一样的。
带线程的 C#:
using System;
using System.Threading;
public class ConcurrentTasks
{
public static void Main(String[] args)
{
var t1 = new Thread(() => DemoTask("+"));
var t2 = new Thread(() => DemoTask("-"));
t1.Start();
t2.Start();
}
private static String DemoTask(String label)
{
for (int i = 0; i < 1000; i++)
{
Console.Write(label);
}
return label + " result";
}
}
最佳答案
粒度可能是原因之一。 TPL 可能具有粗 调度任务的粒度,但 C++ async
实现可能具有精细 粒度。这实质上意味着 C++ 将花费更多时间,因为仅用于处理 +
或 -
它会在两者之间安排另一个任务。
另一个原因可能是 cout
和 Console
是如何实现输出流的。如何锁定以在屏幕上呈现某些内容?正在实现任何缓冲吗?你看,Console.Write
可能正在缓冲,然后在一段时间后打印,但 cout
可能只是立即打印。
因此,您应该做其他事情,而不是依赖于语言的底层 I/O - 可以将字符放在一个共享静态大小的数组中,共享 int
作为共享数组的索引(它们是原子的,不需要锁 用最轻量级的同步原语如读写锁来锁定它)。不,不要使用 vector
或 Array
- 因为在那种情况下,您再次依赖您不知道的东西!
在两种情况下都使用 Release 构建,并尽可能使用相同的优化选项。另外,确保您在同一台机器上运行它们。
关于C# Task.Run() 与 C++ std::async(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37242434/