C# Task.Run() vs. C++ std::async()
C# Task.Run() vs. C++ std::async()
我在Mac上运行两个类似的代码示例,一个在C++中,另一个在C#中。两个并行执行的简单任务(或者至少我希望它们并行执行),一个在循环中打印"+",另一个在环路中打印"-"。我原以为这两个样本的输出非常相似,但令我惊讶的是,它们有点不同。
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#的控制台产生了最大的影响。
我再次修改了C#示例,在执行这两个任务时不使用控制台。相反,我使用了一个共享的输出char数组。结果大不相同,非常接近C++,尽管没有那么完美和一致。
令人惊讶的是,C++程序在每次运行时都能产生完美的+-对,即使在使用cout的情况下也是如此。每次的C#输出都略有不同,我经常看到一个线程在下一个线程开始之前完成。我不得不显著增加迭代次数,因为迭代次数很少,任务总是按顺序运行。
这是更新的C#示例(注意互锁增量!):
using System;
using System.Threading;
using System.Threading.Tasks;
public class ConcurrentTasks
{
private static char[] m_out;
private static int m_index = -1;
public static void Main(String[] args)
{
var iterations = 5000;
m_out = new char[iterations * 2];
var task1 = Task.Run(()=>DemoTask(iterations, '+'));
var task2 = Task.Run(()=>DemoTask(iterations, '-'));
var res1 = task1.Result;
var res2 = task2.Result;
for (int i = 0; i < m_out.Length; i++)
{
Console.Write(m_out[i]);
}
Console.WriteLine("nResults:");
Console.WriteLine(res1);
Console.WriteLine(res2);
}
private static String DemoTask(int iterations, char label)
{
for (int i = 0; i < iterations; i++)
{
int index = Interlocked.Increment(ref m_index);
m_out[index] = label;
}
return label + " result";
}
}
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 为什么std::async使用同一个线程运行函数
- 为什么可以将左值传递给"std::async",即使它引用了右值
- 使用用户定义的参数调用future/async并调用类方法
- 使用"Task"函数指针队列定义作业管理器
- Visual Studio C++ Project File CustomBuild Task: Filter Outp
- 使用 std::async 时死锁,将来作为成员
- 为什么我不能将引用作为 std::async 的函数参数传递
- std::async from std::async in windows xp
- Google Cloud Pubsub Async Streaming API in C++
- -fno-unwind-tables 和 -fno-async-unwind-tables 不起作用 NDK clang
- std::async 如何工作:为什么它会调用这么多次复制/移动?
- std::async 不会立即调用
- async中没有匹配的函数(模板中未解析的类型)
- std::async 如果线程是从 DLL 创建的,则会阻止进程退出?
- 使用 std::vector<std::future<int>> 和 std::async 启动几个线程时中止
- 在Visual Studio中,与std::async一起使用时不调用"thread_local"变量"析构函数,这是一个错误吗?
- 可能的 std::async 实现错误 Windows
- 为什么使用线程池时嵌套调用 boost::async 会阻塞?
- C# Task.Run() vs. C++ std::async()