C#Task.Run()与C ++ std :: async()

时间:2016-05-15 18:57:48

标签: c# c++ concurrency

我在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#with Thread:

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";
    }
}

2 个答案:

答案 0 :(得分:6)

粒度可能是一个原因。 TPL可能具有粒度,用于调度任务,但C ++ async实现可能具有精细粒度。这实际上意味着C ++将花费更多时间,因为只是为了处理+-,它正在安排另一个任务。

另一个原因可能是coutConsole如何实现输出流。如何在屏幕上呈现某些东西?是否正在实施缓冲?你看,Console.Write可能正在缓冲,然后在一段时间后打印,但cout可能只是立即打印它。

因此,您应该执行其他操作,而不是依赖于语言的基础I / O - 可以将该字符放在共享的静态大小的数组上,共享int作为共享数组的索引(它们是原子的,不需要锁用最轻的同步原语(如读写器锁)锁定它。不,不要使用vectorArray - 因为在这种情况下,您再次依赖处理您不了解的事情!

在两种情况下都使用Release版本,并提供尽可能相同的优化选项。另外,请确保您在同一台计算机上运行它们。

答案 1 :(得分:2)

所以看起来C#的控制台影响最大。

我再次修改了C#示例,在执行2个任务时不使用控制台。相反,我使用共享输出字符数组。结果截然不同,非常接近C ++,虽然不是那么完美和一致。

令人惊讶的是,即使使用cout,C ++程序在每次运行时都会产生完美的+ - 对。 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";
    }
}