我正在编写一个游戏,并且使用OpenGL我需要将一些工作卸载到OpenGL上下文处于活动状态的渲染线程中,但其他所有工作都由正常的线程池处理。
有没有办法可以强制在特殊的线程池中执行Task,从async
创建的任何新任务也可以调度到该线程池?
我想要一些专门的渲染线程,我希望能够使用async
和await
来创建和填充顶点缓冲区。
如果我只使用自定义任务调度程序和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);
}
}
的范围?
答案 0 :(得分:1)
这可能与微软为Windows窗体和WPF同步上下文所做的基本相同。
第一部分 - 您处于OpenGL线程中,并希望将一些工作放入线程池中,完成此工作后,您需要重新进入OpenGL线程。
我认为最好的方法是实现自己的SynchronizationContext
。这件事基本上控制了TaskScheduler
的工作方式以及它如何安排任务。默认实现只是将任务发送到线程池。您需要做的是将任务发送到专用线程(保存OpenGL上下文)并在那里逐个执行。
实现的关键是覆盖Post
和Send
方法。预计这两种方法都会执行回调,其中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
)将它们完全删除。