首页 > 解决方案 > 并行和异步 C#

问题描述

我正在用 C# 编写一个控制台应用程序,它需要一组视频并对其进行转码,只要新的 GPU 可以免费使用。运行应用程序的机器有两个 GPU。但我真的很难建立这个。完成这项工作的方法是FireTranscode()

private void FireTranscode(int counter)
  {
   Random rand = new Random();
   int gpu;

   lock (thisLock)
   {
    gpu = PickGPU(0) == true ? 0 : 1;
    GPU[gpu] = false;
    if (gpu == 0) { gpuZero += 1; } else { gpuOne += 1; };
    Thread.Sleep(rand.Next(1, 5));
    videos -= 1;
   }

   Console.WriteLine($"Transconding on {gpu} using thread: {Thread.CurrentThread.ManagedThreadId}  {transcodeArray[Convert.ToInt32(counter), 2]}");
    GPU[gpu] = true;
}

它由 ManageTranscode() 触发

private async void ManageTrancode()
  {
   for(counter=0; counter < videos; counter++)
   {
    if (GPU[0] == false & GPU[1] == false)
    {
     await Task.WhenAny(transcodeList);
    }
    else
    {
     transcodeList.Add(Task.Factory.StartNew(() => FireTranscode(counter)));
    }
   }
  }

它假设调用 FireTranscode 后跟参数计数器,40 次异步(视频变量的值),并且如果两个 GPU(static Dictionary<int, bool> GPU = new Dictionary<int, bool> { { 0, true }, { 1, true } };都在使用中(=false)它应该等到任何任务完成并免费使用(=true) .

我正在尝试学习如何正确使用它,我将不胜感激一些提示并帮助实现这一目标。谢谢你。

标签: c#parallel-processing

解决方案


通过使用以下代码,您可以简化逻辑并使其在可用 GPU 方面更具可扩展性。它使用SemaphoreSlim(@Poul Bak 也提到)允许通过定义的参数实现并行度。

此外,我已重构您的代码以将 GPU 作为类(您也可以使用 Struct)。

private object lockObj = new object();
private List<GPU> availableGPUs = List<GPU>() { /* initialize GPUs here */}; 
private int AvailableGPUCount { get { return availableGPUs.Count(); } }
private async void ManageTrancode()
{
    int maxThread = AvailableGPUCount;
    SemaphoreSlim lockSlim = new SemaphoreSlim(maxThread, maxThread);
    for(int counter = 0; counter < videos; counter++)
    {
        await lockSlim.WaitAsync();
        await Trancode();
        lockSlim.Release();
    }
}


private async Task Trancode()
{
    GPU gpu = GetAndLockGPU(); 
    // await <<Run actual trancode here>>
    ReleaseGPU(gup.Id);
}

private GPU GetAndLockGPU()
{
    GPU gpu = null;
    lock (lockObj)
    {
        gpu = availableGPUs.First(g => g.IsAvailable);
        gpu.IsAvailable = false;
    }
    
    return gpu; 
}

private void ReleaseGPU(int gpuId)
{
    lock (lockObj)
    {
        availableGPUs.First(g => g.Id == gpuId).IsAvailable = true;
    }
}

private class GPU
{
    public int Id {get; set;}
    public bool IsAvailable {get; set;} = true;
}

推荐阅读