我怎样才能有两个单独的任务调度程序?

时间:2015-09-23 13:45:10

标签: c# multithreading async-await task-parallel-library threadpool

我正在编写一个游戏,并且使用OpenGL我需要将一些工作卸载到OpenGL上下文处于活动状态的渲染线程中,但其他所有工作都由正常的线程池处理。

有没有办法可以强制在特殊的线程池中执行Task,从async创建的任何新任务也可以调度到该线程池?

我想要一些专门的渲染线程,我希望能够使用asyncawait来创建和填充顶点缓冲区。

如果我只使用自定义任务调度程序和new Factory(new MyScheduler()),似乎任何后续的Task对象都将被调度到线程池,而Task.Factory.Scheduler突然null public async Task Initialize() { // The two following tasks should run on the rendering thread pool // They cannot run synchronously because that will cause them to fail. this.VertexBuffer = await CreateVertexBuffer(); this.IndexBuffer = await CreateIndexBuffer(); // This should be dispatched, or run synchrounousyly, on the normal thread pool Vertex[] vertices = CreateVertices(); // Issue task for filling vertex buffer on rendering thread pool var fillVertexBufferTask = FillVertexBufffer(vertices, this.VertexBuffer); // This should be dispatched, or run synchrounousyly, on the normal thread pool short[] indices = CreateIndices(); // Wait for tasks on the rendering thread pool to complete. await FillIndexBuffer(indices, this.IndexBuffer); await fillVertexBufferTask; // Wait for the rendering task to complete. }

以下代码应显示我希望能够执行的操作:

async

有没有办法实现这一目标,还是超出await / import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class MazeGame { protected static int row; public static void main(String[] args) throws IOException { getVariables(); } public int getRow() { return MazeGame.row; } static void getVariables() throws IOException { InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); System.out.println("Enter a value for row : "); String strRow = br.readLine(); MazeGame.row = Integer.parseInt(strRow); Location ln = new Location(); ln.getVariables(); } } import java.io.IOException; public class Location { MazeGame Locationx = new MazeGame(); public Location() { } public void getVariables() throws IOException { System.out.println("The value of class variable row, initially is: "+MazeGame.row); // class variable Locationx.row = 21; // object variable System.out.println("The value of object variable row, after modification is: "+Locationx.row); MazeGame.row = 23; // class variable System.out.println("The value of class variable row, after modification is: "+MazeGame.row); } } 的范围?

2 个答案:

答案 0 :(得分:1)

这可能与微软为Windows窗体和WPF同步上下文所做的基本相同。

第一部分 - 您处于OpenGL线程中,并希望将一些工作放入线程池中,完成此工作后,您需要重新进入OpenGL线程。

我认为最好的方法是实现自己的SynchronizationContext。这件事基本上控制了TaskScheduler的工作方式以及它如何安排任务。默认实现只是将任务发送到线程池。您需要做的是将任务发送到专用线程(保存OpenGL上下文)并在那里逐个执行。

实现的关键是覆盖PostSend方法。预计这两种方法都会执行回调,其中Send必须等待调用完成,Post不会。使用线程池的示例实现是Send只是直接调用回调,Post将回调委托给线程池。

对于OpenGL线程的执行队列,我认为查询BlockingCollection的线程应该很好。只需将回调发送到此队列即可。如果从错误的线程调用post方法并且您需要等待任务完成,您可能还需要一些回调。

但总的来说这一切都应该有效。 async / await可确保在线程池中执行的异步调用之后恢复SynchronizationContext。因此,在将一些工作放入另一个线程之后,您应该能够返回到OpenGL线程。

第二部分 - 您在另一个线程中,想要将一些工作发送到OpenGL线程并等待完成该工作。

这也是可能的。在这种情况下,我的想法是您不使用Task s,而是使用其他等待的对象。通常,每个对象都可以等待。它只需要实现一个公共方法getAwaiter(),它返回一个实现INotifyCompletion接口的对象。 await做的是将剩余的方法放入新的Action并将此操作发送到该接口的OnCompleted方法。一旦等待的操作完成,预计等待者将调用预定的动作。此等待者还必须确保捕获SynchronizationContext并在捕获的SynchronizationContext上执行延续。这听起来很复杂,但是一旦掌握了它,就会变得相当容易。对我有很大帮助的是YieldAwaiter的参考源(基本上如果使用await Task.Yield()会发生这种情况)。这不是你需要的,但我认为这是一个开始的地方。

返回awaiter的方法必须注意将实际工作发送到必须执行它的线程(你可能已经有了第一部分的执行队列),并且一旦工作完成,awaiter必须触发

<强>结论

毫无疑问。这是很多工作。但是如果你做了所有这些,你就会有更少的问题,因为你可以无缝地使用async / await模式,就像你在Windows窗体或WPF中工作一样,这是一个色调加。

答案 1 :(得分:1)

首先,在调用方法之后,意识到await引入了特殊行为;也就是说,这段代码:

this.VertexBuffer = await CreateVertexBuffer();

与此代码几乎相同:

var createVertexBufferTask = CreateVertexBuffer();
this.VertexBuffer = await createVertexBufferTask;

因此,您必须明确安排代码以在不同的上下文中执行方法。

您提到使用MyScheduler,但我看不到您的代码使用它。这样的事情应该有效:

this.factory = new TaskFactory(CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.None, new MyScheduler());

public async Task Initialize()
{
  // Since you mention OpenGL, I'm assuming this method is called on the UI thread.

  // Run these methods on the rendering thread pool.
  this.VertexBuffer = await this.factory.StartNew(() => CreateVertexBuffer()).Unwrap();
  this.IndexBuffer = await this.factory.StartNew(() => CreateIndexBuffer()).Unwrap();

  // Run these methods on the normal thread pool.
  Vertex[] vertices = await Task.Run(() => CreateVertices());
  var fillVertexBufferTask = Task.Run(() => FillVertexBufffer(vertices, this.VertexBuffer));
  short[] indices = await Task.Run(() => CreateIndices());
  await Task.Run(() => FillIndexBuffer(indices, this.IndexBuffer));

  // Wait for the rendering task to complete.
  await fillVertexBufferTask;
}

我会考虑组合这些多个Task.Run调用,或者(如果在正常线程池线程上调用Initialize)将它们完全删除。