如何在.NET中切换到主线程(如果我使用自己的游戏循环)?
给定:
- 游戏循环在控制台应用的主线程上运行。
- 游戏动作/事件在其他线程上发射。
问题:
- 如何将这些操作/事件切换到主线程?
接下来,我将进行实施。但是我不喜欢它,原因有几个:
- 我的自定义任务。我只是担心我可能写错了。
- 没有同步上下文。
- 游戏循环每毫秒每毫秒滴答滴答,但是如果它对新的动作/事件做出反应会更好。
那么,有更好的方法可以做到吗?
class Program {
private static async Task Main(string[] args) {
var game = new Game();
new Timer( i => game.ActionAsync().Wait(), null, 0, 3000 );
new Timer( i => game.EventAsync().Wait(), null, 0, 3000 );
game.Run();
}
}
public class Game {
private GameTaskScheduler TaskScheduler { get; } = new GameTaskScheduler();
public Game() {
}
// Run game loop
public void Run() {
while (true) {
TaskScheduler.Execute();
// Game logic
Console.WriteLine( "Update: {0:00}", Environment.CurrentManagedThreadId );
Thread.Sleep( 1000 );
}
}
// User action
public async Task ActionAsync() {
await SwitchToMainThread();
Console.WriteLine( "Action: {0:00}", Environment.CurrentManagedThreadId );
}
// User event
public async Task EventAsync() {
await SwitchToMainThread();
Console.WriteLine( "Event: {0:00}", Environment.CurrentManagedThreadId );
}
// Utils
public AwaitExtensions.TaskSchedulerAwaitable SwitchToMainThread() {
return TaskScheduler.SwitchTo();
}
}
// todo: Can I do it without custom TaskScheduler?
internal class GameTaskScheduler : TaskScheduler {
private ConcurrentQueue<Task> Tasks { get; } = new ConcurrentQueue<Task>();
public override int MaximumConcurrencyLevel => 1;
public void Execute() {
while (Tasks.TryDequeue( out var task )) {
TryExecuteTask( task );
}
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) {
return false;
}
protected override void QueueTask(Task task) {
Tasks.Enqueue( task );
}
protected sealed override bool TryDequeue(Task task) {
return false;
}
protected override IEnumerable<Task>? GetScheduledTasks() {
return Tasks.ToArray();
}
}
Given:
- Game loop is running on the main thread of console app.
- Game actions/events are fired on other threads.
Question:
- How can I switch/forward those actions/events to the main thread?
Next I will give my implementation. But I don't like it for several reasons:
- My custom TaskScheduler. I'm just afraid that I might write something wrong.
- No synchronization context.
- Game loop ticks every N milliseconds but it would be better if it reacted to new actions/events.
So, are there a better ways to do this?
class Program {
private static async Task Main(string[] args) {
var game = new Game();
new Timer( i => game.ActionAsync().Wait(), null, 0, 3000 );
new Timer( i => game.EventAsync().Wait(), null, 0, 3000 );
game.Run();
}
}
public class Game {
private GameTaskScheduler TaskScheduler { get; } = new GameTaskScheduler();
public Game() {
}
// Run game loop
public void Run() {
while (true) {
TaskScheduler.Execute();
// Game logic
Console.WriteLine( "Update: {0:00}", Environment.CurrentManagedThreadId );
Thread.Sleep( 1000 );
}
}
// User action
public async Task ActionAsync() {
await SwitchToMainThread();
Console.WriteLine( "Action: {0:00}", Environment.CurrentManagedThreadId );
}
// User event
public async Task EventAsync() {
await SwitchToMainThread();
Console.WriteLine( "Event: {0:00}", Environment.CurrentManagedThreadId );
}
// Utils
public AwaitExtensions.TaskSchedulerAwaitable SwitchToMainThread() {
return TaskScheduler.SwitchTo();
}
}
Discussion about TaskScheduler
// todo: Can I do it without custom TaskScheduler?
internal class GameTaskScheduler : TaskScheduler {
private ConcurrentQueue<Task> Tasks { get; } = new ConcurrentQueue<Task>();
public override int MaximumConcurrencyLevel => 1;
public void Execute() {
while (Tasks.TryDequeue( out var task )) {
TryExecuteTask( task );
}
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) {
return false;
}
protected override void QueueTask(Task task) {
Tasks.Enqueue( task );
}
protected sealed override bool TryDequeue(Task task) {
return false;
}
protected override IEnumerable<Task>? GetScheduledTasks() {
return Tasks.ToArray();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是带有锁定和手动Resetevent的版本,允许您等待任务。
看起来它有效,但恐怕在某些极少数情况下可能会有错误。你能告诉我我做对了吗?
讨论有关taskscheduler的讨论
Here is the version with locks and ManualResetEvent allowing you to wait for a task.
It looks like it works, but I'm afraid there might be bugs in some rare cases. Can you tell me if I did it right?
Discussion about TaskScheduler