异步任务,它不会在空闲时阻塞线程

时间:2015-08-11 08:26:49

标签: c# multithreading asynchronous

我正在构建一个具有标准循环的游戏引擎(更新/绘制每秒调用60次)。我需要一个后台任务,当我的主线程忙时,它会工作。我对并发性知之甚少,以下是我可以提出的选项:

选项A:

    // Runs once.
    protected override void Initialize()
    {
        Scene.BGThreadTrigger = new ManualResetEventSlim(false);
        Scene.BGTask = Task.Run((Action)Scene.BGThreadFunction);
    }

    // Runs 60 times per second.
    protected override void Update()
    {
        BGThreadTrigger.Set();
    }
    public static ConcurrentQueue<Action> BGThreadTaskContainer = new ConcurrentQueue<Action>();

    public static Task BGEventWait()
    {
        BGThreadTrigger.Wait();
        return Task.FromResult(false);
    }

    public static async void BGThreadFunction()
    {
        Action action;
        while (ZHiGame.GameIsRunning && SceneIsAlive)
        {
            await BGEventWait();

            while (BGThreadTaskContainer.TryDequeue(out action))
                action();
            BGThreadTrigger.Reset();
        }
    }

选项B:

    public static ConcurrentQueue<Action> BGThreadTaskContainer = new ConcurrentQueue<Action>();

    // Runs 60 times per second.
    protected override void Update()
    {
        Scene.BGTask = Task.Run((Action)Scene.BGThreadFunction);
    }    

    public static async void BGThreadFunction()
    {
        Action action;
        while (BGThreadTaskContainer.TryDequeue(out action))
            action();
    }

我想知道有经验的编码人员对并发工作的看法 - 实现这一点的最佳方式是什么。

更新:我需要在特定时间点严格执行BGThreadFunction中的代码 - 当Draw()方法执行时。我面临的限制是Draw()方法只在主线程上执行。但是有一些工作可以在执行时完成,所以我需要的是一种收集工作的方法(一个容器)以及一种在给定时间点触发它的方法。我不需要一个容器,一旦添加它们就异步执行任务,因为通常在添加它们时(我的例子,遗憾的是没有显示)所有线程都很忙,即使它们不是,我仍然需要一种方法控制容器被清空和处理的时间。

更新:收到评论后,我设法创建了以下系统:

    public static ActionBlock<Action> WorkerBlock;
    // Runs once.
    protected override void Initialize()
    {
        Scene.BGThreadTrigger = new ManualResetEventSlim(false);
        Scene.BGTask = Task.Run((Action)Scene.BGThreadFunction);
    }

    public static ConcurrentQueue<Action> BGThreadTaskContainer = new ConcurrentQueue<Action>();

    // Runs 60 times per second.
    protected override void Update()
    {
        Action action;

        // This way I signal the start of the background work each frame.
        while (BGThreadTaskContainer.TryDequeue(out action))
            WorkerBlock.Post(action);
    }

更新:我也试过这个解决方案:

    Thread BGThread;

    // Runs 60 times per second.
    protected override async void Update()
    {
        if (BGThread != null)
            BGThread.Wait();
        BGThread = Task.Run((Action)WhileDrawIsExecuted);
    }


    public static void WhileDrawIsExecuted()
    {
        Parallel.ForEach(BGThreadTaskContainer, action =>
        {
            action();
        });
        Action completedAction;
        while (BGThreadTaskContainer.TryDequeue(out completedAction)) ;
    }

我认为这个是更好的,但我不确定Task.Run()是否可以安全地调用(每秒60次)。

0 个答案:

没有答案