多线程代码中频繁出现延迟峰值

发布于 2024-10-21 21:47:39 字数 3557 浏览 1 评论 0原文

我正在尝试使我的简单 C# 图形库成为多线程。然而,在引入此代码之后:

/* foreach (IAffector affector in affectorLookup.Values)
    affector.Update(timestep); */

taskManager.Value = timestep; taskManager.Start();

foreach (IAffector affector in affectorLookup.Values)
    taskManager.AddToQueue(affector.Update);
taskManager.StopWhenDone();
taskManager.Wait();

模拟开始经历急剧的滞后尖峰,这似乎源自 TaskHandler.Run (我不能确定,因为添加前面的代码使我的代码分析器忽略 TaskHandler.Run 之外的任何内容)。

任务管理器:

public class TaskManager
{
    public delegate void MethodDel(float timestep);
    private Queue<MethodDel> queue;
    private List<TaskHandler> handlers;
    private float value;


    public float Value
    {
        get
        {
            return value;
        }
        set
        {
            this.value = value;
        }
    }


    public TaskManager()
    {
        this.queue = new Queue<MethodDel>();
        this.handlers = new List<TaskHandler>(System.Environment.ProcessorCount);

        for (int t = 0; t < this.handlers.Capacity; ++t)
            this.handlers.Add(new TaskHandler(this));

        this.value = 0;
    }


    public void Start()
    {
        foreach (var handler in handlers)
            handler.Start();
    }


    public void Stop()
    {
        lock (queue)
            queue.Clear();

        foreach (var handler in handlers)
            handler.StopWhenDone();
    }


    public void StopWhenDone()
    {
        foreach (var handler in handlers)
            handler.StopWhenDone();
    }


    public void AddToQueue(MethodDel method)
    {
        lock (queue)
            queue.Enqueue(method);
    }


    public bool GetFromQueue(out MethodDel method)
    {
        lock (queue)
        {
            if (queue.Count == 0) { method = null; return false; }

            method = queue.Dequeue();
            return true;
        }
    }


    public int GetQueueCount()
    {
        return queue.Count;
    }

    internal void Wait()
    {
        // Have to wait for them one at a time because the main thread is STA.

        WaitHandle[] waitHandles = new WaitHandle[1];
        // for (int t = 0; t < handlers.Count; ++t)
            // waitHandles[t] = handlers[t].WaitHandle;

        // WaitHandle.WaitAll(waitHandles);
        for (int t = 0; t < handlers.Count; ++t)
        { waitHandles[0] = handlers[t].WaitHandle; WaitHandle.WaitAll(waitHandles); }
    }
}

和任务处理程序:

public class TaskHandler
{
    private TaskManager manager;
    private Thread thread;
    private bool stopWhenDone;
    private ManualResetEvent waitHandle;


    public ManualResetEvent WaitHandle
    {
        get
        {
            return waitHandle;
        }
    }


    public TaskHandler(TaskManager manager)
    {
        this.manager = manager;
    }


    public void Start()
    {
        waitHandle = new ManualResetEvent(false);

        stopWhenDone = false;

        thread = new Thread(Run);
        thread.IsBackground = true;
        thread.SetApartmentState(ApartmentState.MTA);
        thread.Start();
    }


    public void StopWhenDone()
    {
        this.stopWhenDone = true;
    }

    // Possible source of slowdown
    private void Run()
    {
        TaskManager.MethodDel curMethod;
        while (!stopWhenDone || manager.GetQueueCount() > 0)
        {
            if (manager.GetFromQueue(out curMethod))
            {
                curMethod(manager.Value);
            }
        }
        waitHandle.Set();
    }
}

I'm attempting to make my simple C# graphics library multi-threaded. However, after the introduction of this code:

/* foreach (IAffector affector in affectorLookup.Values)
    affector.Update(timestep); */

taskManager.Value = timestep; taskManager.Start();

foreach (IAffector affector in affectorLookup.Values)
    taskManager.AddToQueue(affector.Update);
taskManager.StopWhenDone();
taskManager.Wait();

the simulation starts experiencing sharp lag-spikes, which seem to originate in TaskHandler.Run (I can't tell for sure, because adding the previous code makes my code profiler ignore anything outside TaskHandler.Run).

The task manager:

public class TaskManager
{
    public delegate void MethodDel(float timestep);
    private Queue<MethodDel> queue;
    private List<TaskHandler> handlers;
    private float value;


    public float Value
    {
        get
        {
            return value;
        }
        set
        {
            this.value = value;
        }
    }


    public TaskManager()
    {
        this.queue = new Queue<MethodDel>();
        this.handlers = new List<TaskHandler>(System.Environment.ProcessorCount);

        for (int t = 0; t < this.handlers.Capacity; ++t)
            this.handlers.Add(new TaskHandler(this));

        this.value = 0;
    }


    public void Start()
    {
        foreach (var handler in handlers)
            handler.Start();
    }


    public void Stop()
    {
        lock (queue)
            queue.Clear();

        foreach (var handler in handlers)
            handler.StopWhenDone();
    }


    public void StopWhenDone()
    {
        foreach (var handler in handlers)
            handler.StopWhenDone();
    }


    public void AddToQueue(MethodDel method)
    {
        lock (queue)
            queue.Enqueue(method);
    }


    public bool GetFromQueue(out MethodDel method)
    {
        lock (queue)
        {
            if (queue.Count == 0) { method = null; return false; }

            method = queue.Dequeue();
            return true;
        }
    }


    public int GetQueueCount()
    {
        return queue.Count;
    }

    internal void Wait()
    {
        // Have to wait for them one at a time because the main thread is STA.

        WaitHandle[] waitHandles = new WaitHandle[1];
        // for (int t = 0; t < handlers.Count; ++t)
            // waitHandles[t] = handlers[t].WaitHandle;

        // WaitHandle.WaitAll(waitHandles);
        for (int t = 0; t < handlers.Count; ++t)
        { waitHandles[0] = handlers[t].WaitHandle; WaitHandle.WaitAll(waitHandles); }
    }
}

And the task handler:

public class TaskHandler
{
    private TaskManager manager;
    private Thread thread;
    private bool stopWhenDone;
    private ManualResetEvent waitHandle;


    public ManualResetEvent WaitHandle
    {
        get
        {
            return waitHandle;
        }
    }


    public TaskHandler(TaskManager manager)
    {
        this.manager = manager;
    }


    public void Start()
    {
        waitHandle = new ManualResetEvent(false);

        stopWhenDone = false;

        thread = new Thread(Run);
        thread.IsBackground = true;
        thread.SetApartmentState(ApartmentState.MTA);
        thread.Start();
    }


    public void StopWhenDone()
    {
        this.stopWhenDone = true;
    }

    // Possible source of slowdown
    private void Run()
    {
        TaskManager.MethodDel curMethod;
        while (!stopWhenDone || manager.GetQueueCount() > 0)
        {
            if (manager.GetFromQueue(out curMethod))
            {
                curMethod(manager.Value);
            }
        }
        waitHandle.Set();
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

话少情深 2024-10-28 21:47:39

启动线程是一项繁重的操作。不确定它是否像您所经历的那么重,但可能就是这样。此外,让所有处理并行运行可能会给您的系统带来很大的压力,而且可能没有什么好处......

Starting a thread is a heavy operation. Not sure if it's as heavy as you are experiencing, but that could be it. Also, having all your processing run parallel can be putting a big strain on your system with possibly little benefit...

与他有关 2024-10-28 21:47:39

我敢说尖峰与 waitHandle.Set(); 有关。

我喜欢整体设计,但我之前没有使用过 WaitHandle,所以我不确定它如何与您的设计交互。

I'm going to venture that the spikes have something to do with waitHandle.Set();

I like the overall design, but I have not used WaitHandle before, so I am unsure how this interacts with your design.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文