如何在c#中正确排队和执行具有可变开始时间的任务?

如何在c#中正确排队和执行具有可变开始时间的任务?,c#,multithreading,C#,Multithreading,我还是个新手。目前正在使用.NET3.5。我正在写一个在线游戏,我的游戏引擎需要处理玩家要求的单位移动。这些单位将以不同的速度移动,并且可能有不同的目的地,因此单位到达(以及命令完成)的实际时间将大不相同 我的策略是,该模型将计算沿着给定路径的下一步移动,然后排队等待工作人员在该单元到达该位置的同时执行。工人将更新单位、通知玩家、排队等待下一步等 我最大的障碍之一是编写一个有条件执行任务的工作线程。在过去,我的工作线程只是尝试按顺序尽快运行每个任务。另一个挑战是,单元移动可能排队,其开始时间早于

我还是个新手。目前正在使用.NET3.5。我正在写一个在线游戏,我的游戏引擎需要处理玩家要求的单位移动。这些单位将以不同的速度移动,并且可能有不同的目的地,因此单位到达(以及命令完成)的实际时间将大不相同

我的策略是,该模型将计算沿着给定路径的下一步移动,然后排队等待工作人员在该单元到达该位置的同时执行。工人将更新单位、通知玩家、排队等待下一步等

我最大的障碍之一是编写一个有条件执行任务的工作线程。在过去,我的工作线程只是尝试按顺序尽快运行每个任务。另一个挑战是,单元移动可能排队,其开始时间早于队列中的任何其他任务,因此显然,它们需要移动到队列的最前面

这是我到目前为止发现的,它可以工作,但有一些限制,即它的100毫秒睡眠限制了我的命令响应时间。我想做的事情是否有规律,或者我是否遗漏了一些明显的东西

谢谢大家!

public class ScheduledTaskWorker
{
    List<ScheduledTask> Tasks = new List<ScheduledTask>();

    public void AddTask(ScheduledTask task)
    {
        lock (Tasks)
        {
            Tasks.Add(task);
            Tasks.Sort();
        }
    }

    private void RemoveTask(ScheduledTask task)
    {
        lock (Tasks)
        {
            Tasks.Remove(task);
        }
    }

    private ScheduledTask[] CopyTasks()
    {
        lock (Tasks)
        {
            return Tasks.ToArray();
        }
    }


    public void DoWork()
    {
        while (!StopWorking)
        {
            ScheduledTask[] safeCopy = CopyTasks();

            foreach (ScheduledTask task in safeCopy)
            {
                if (task.ExecutionTime > DateTime.Now) 
                    break;

                if (ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadPoolCallBack), task))
                    RemoveTask(task);
                else
                {
                    // if work item couldn't be queued, stop queuing and sleep for a bit
                    break;
                }
            }

            // sleep for 100 msec so we don't hog the CPU. 
            System.Threading.Thread.Sleep(100);
        }
    }

    static void ThreadPoolCallBack(Object stateInfo)
    {
        ScheduledTask task = stateInfo as ScheduledTask;
        task.Execute();
    }

    public void RequestStop()
    {
        StopWorking = true;
    }

    private volatile bool StopWorking;
}
公共类ScheduledTaskWorker
{
列表任务=新列表();
公共void AddTask(ScheduledTask任务)
{
锁定(任务)
{
任务。添加(任务);
Tasks.Sort();
}
}
私有void RemoveTask(ScheduledTask任务)
{
锁定(任务)
{
任务。删除(任务);
}
}
专用ScheduledTask[]CopyStasks()
{
锁定(任务)
{
返回任务。ToArray();
}
}
公共工作
{
当(!停止工作)
{
ScheduledTask[]safeCopy=CopyStasks();
foreach(safeCopy中的ScheduledTask任务)
{
如果(task.ExecutionTime>DateTime.Now)
打破
if(ThreadPool.QueueUserWorkItem(新建WaitCallback(ThreadPoolCallBack),任务))
RemoveTask(任务);
其他的
{
//如果工作项无法排队,请停止排队并休眠一会
打破
}
}
//睡眠100毫秒,这样我们就不会占用CPU。
系统线程线程睡眠(100);
}
}
静态void ThreadPoolCallBack(对象状态信息)
{
ScheduledTask任务=stateInfo作为ScheduledTask;
task.Execute();
}
公共void RequestStop()
{
停止工作=正确;
}
私人易变bool停止工作;
}
而任务本身

public class ScheduledTask : IComparable
{
    Action<Item> Work;
    public DateTime ExecutionTime;
    Item TheItem;

    public ScheduledTask(Item item, Action<Item> work, DateTime executionTime)
    {
        Work = work;
        TheItem = item;
        ExecutionTime = executionTime;
    }

    public void Execute()
    {
        Work(TheItem);
    }

    public int CompareTo(object obj)
    {
        ScheduledTask p2 = obj as ScheduledTask;
        return ExecutionTime.CompareTo(p2.ExecutionTime);
    }

    public override bool Equals(object obj)
    {
        ScheduledTask p2 = obj as ScheduledTask;
        return ExecutionTime.Equals(p2.ExecutionTime);
    }

    public override int GetHashCode()
    {
        return ExecutionTime.GetHashCode();
    }
}
公共类ScheduledTask:IComparable
{
行动工作;
公共日期时间执行时间;
项目1:项目6;
公共ScheduledTask(项目、操作工时、日期时间执行时间)
{
工作=工作;
ITEEM=项目;
ExecutionTime=执行时间;
}
public void Execute()
{
工作(ITEEM);
}
公共整数比较(对象对象对象)
{
ScheduledTask p2=作为ScheduledTask的obj;
返回ExecutionTime.CompareTo(p2.ExecutionTime);
}
公共覆盖布尔等于(对象对象对象)
{
ScheduledTask p2=作为ScheduledTask的obj;
返回ExecutionTime.Equals(p2.ExecutionTime);
}
公共覆盖int GetHashCode()
{
返回ExecutionTime.GetHashCode();
}
}

听起来非常脆弱。在不了解更多游戏及其环境的情况下,听起来你可能仅仅通过移动大量单元并让它们执行长时间运行的任务就让计算机淹没在新线程中。如果这是服务器端代码,并且任何数量的玩家都可以同时使用它,这几乎保证了线程过多的问题。此外,还有时间问题。复杂的代码、错误的估计、启动给定线程的延迟(这将在线程池中发生)或大量线程同时执行的速度减慢都可能会延迟任务实际执行的时间


据我所知,大多数游戏都是单线程的。这是为了减少这些问题,以及让一切更加同步。如果操作发生在不同的线程上,它们可能会收到不公平的优先级,并且运行不可预测,从而使游戏变得不公平。通常,单个游戏循环会遍历所有动作,并以基于时间的方式执行它们(即,单位Bob每秒可以移动1平方米,他的精灵以每秒15帧的速度动画)。例如,绘制图像/设置动画、移动单元、执行碰撞检测和运行任务都可能在每次循环中发生。这也消除了时间问题,因为所有事情都将获得相同的优先级(无论他们在做什么或移动速度有多快)。这也意味着单位在到达目的地的那一刻就会知道,并且可以执行它应该执行的任何任务,每次迭代一点。

我并不反对克里斯,他的答案可能更适合你的潜在问题

但是,为了回答您的具体问题,您可以使用与Java相当的东西,它看起来已经移植到C#这里:


您的
Task
类将实现
IDelayed
,然后您可以使用一个线程调用Take(在没有项目准备就绪时阻塞)并执行任务,或者将它们传递给
ThreadPool.QueueUserWorkItem

谢谢Chris,很好的建议,是的,它是服务器端代码。你给了我很多考虑。我很难相信多人游戏引擎会像你提到的那样是单线程的。对于客户端来说,我可以相信这一点。我将就绪命令的执行旋转到线程池,因为我没有