【问题标题】:Converting .NET 4 Threading to .NET 2将 .NET 4 线程转换为 .NET 2
【发布时间】:2013-12-18 07:26:54
【问题描述】:

我正在使用其他人的 .NET 4 开源平滑粒子流体动力学代码,我正在尝试将其转换为 Unity 项目,您只能指望它符合 .NET 2 标准。不幸的是,代码使用了 Parallels 类(太棒了!),但他使用了一种更晦涩难懂的重载。任何人都可以看到在 .NET 2 中实现相同目标而又不会对性能造成巨大影响的好方法吗?

    Parallel.For(
        0,
        _numActiveParticles,
        () => new Vector2[MAX_PARTICLES],
        (i, state, accumulatedDelta) => calculateForce(_activeParticles[i], accumulatedDelta),
        (accumulatedDelta) =>
        {
            lock (_calculateForcesLock)
            {
                for (int i = _numActiveParticles - 1; i >= 0; i--)
                {
                    int index = _activeParticles[i];
                    _delta[index] += accumulatedDelta[index] / MULTIPLIER;
                }
            }
        }
    );

认为这就是代码的作用(非线程):

    for (int i = 0; i < _numActiveParticles; i++)
    {
        Vector2[] temp = new Vector2[MAX_PARTICLES];
        temp = calculateForce(_activeParticles[i], temp);


        for (int k = _numActiveParticles - 1; k >= 0; k--)
        {
            int index = _activeParticles[k];
            _delta[index] += temp[index] / MULTIPLIER;
        }
    }

【问题讨论】:

  • 您可能会觉得这很有帮助stackoverflow.com/q/12386686/809009
  • @OndrejJanacek 谢谢,这很有帮助,但我也不是 100% 确定 Parallels 版本在做什么,所以我想确保我翻译正确。
  • 我不认为你的理解是正确的。您甚至没有在任何地方使用“temp”变量。阅读:msdn.microsoft.com/en-us/library/ff963547.aspx
  • 我认为 unity 几乎是 3.5,虽然它说 2.0。这是一个相关链接answers.unity3d.com/questions/472726/…
  • 如果您要在整个身体上使用lock,那么使用Parallel.For 有什么意义?您也可以使用普通的for。它的性能会更好,因为它不会有所有不必要的同步/线程开销。

标签: c# .net multithreading unity3d game-physics


【解决方案1】:

您的第二个代码不正确。我认为正确的代码是这样的:

var accumulatedDelta= new Vector2[MAX_PARTICLES];

for(int i = 0; i < _numActiveParticles; ++i)
{
    accumulatedDelta = calculateForce(_activeParticles[i], accumulatedDelta);
}

for (int i = _numActiveParticles - 1; i >= 0; i--)
{
    int index = _activeParticles[i];
    _delta[index] += accumulatedDelta[index] / MULTIPLIER;
}

我不知道.net2 有什么,什么没有。但是你可以自己模拟Parallel.For

Parallel.For 重载的解释如下:

第一个参数:循环的开始索引

第二个参数:循环结束索引

第三个参数:将创建任务本地数据的委托。对于Parallel.For 使用的每个线程(一个任务),该委托将被调用并返回localInit 数据。

第四个参数:作为for 的主体的委托。在第一次执行 body 委托时,此委托将检索由 previuse 委托 (localInit) 创建的数据。在每个后续循环中,主体委托可以更改localInit,然后将其返回到下一个主体执行。在 body 委托的最后一次执行中,localInit 数据将被传递给最后一个委托。

最后一个参数:当任务完成时,每个任务都会调用另一个委托。 localInit 将传递给此代表。因为这个委托可以被多个任务并发调用,所以你必须保护你的共享数据。

编辑:

ParallelFor 的一个版本可以是这样的:

public static void ParallerFor<TLocal>(int startIndex, int endIndex, Func<TLocal> initData, Func<int, TLocal, TLocal> body, Action<TLocal> finalizer)
    {
        int numThreads = Environment.ProcessorCount;
        int chunkOffset = ((endIndex - startIndex) / numThreads) + 1;

        Task[] tasks = new Task[numThreads];

        Enumerable.Range(0, numThreads).ToList().ForEach(x =>
            {
                int start = x * chunkOffset;
                int end = ((x + 1) * chunkOffset);
                end = end > endIndex ? endIndex : end;

                tasks[x] = Task.Factory.StartNew(() =>
                {
                    TLocal init = initData();

                    for(int i = start; i < end; ++i)
                    {
                        init = body(i, init);
                    }

                    finalizer(init);
                });
            });

        Task.WhenAll(tasks).Wait();
    }

【讨论】:

  • 使用Task类重新实现Parallel.For是一种心情。如果你有一个,你也有另一个(都是 .net 4 及更高版本)。
  • @Christian.K 感谢您的提及。我不知道.net 2 有什么样的并行助手,但是将task 替换为手动thread 启动是一项简单的工作。
  • 你可以查看msdn。它会告诉你。请注意,任务与线程不同(至少使用线程池而不是普通线程)。通常,重新实现 TPL 功能对于获得正确和高效来说并非易事。网上有很多关于这方面的资料。
猜你喜欢
  • 1970-01-01
  • 2011-08-06
  • 1970-01-01
  • 2013-02-27
  • 1970-01-01
  • 2017-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多