【问题标题】:Run a Thread in an infinite loop without running out of memory如何在另一个线程中运行循环中的方法
【发布时间】:2022-01-04 04:11:27
【问题描述】:

我有一个名为Update 的方法,它是从for(; ;) 循环中调用的,
在该方法中,我有另一个循环遍历列表的 for 循环,

for (int i = 0;i<LIST.Count;i++) { /* Code */ }

在那个循环中,我调用了一个方法,如果它运行会减慢进程(~250 FPS 到 ~55 FPS)。
如果我这样做了

for (int i = 0;i<LIST.Count;i++) { new Thread(() => /* Method */ ).Start(); }

它用尽了所有线程并且进程崩溃了。我如何在不使用所有线程的情况下运行该方法。

重要
我在System 中几乎不能使用任何东西。

【问题讨论】:

  • 你可能想看看使用Parallel.ForEach()
  • ThreadPool.QueueUserWorkItem 方法可能是一个起点。 “我不能在 System.Threading.Tasks 中使用任何东西”
  • 我不能使用System.Threading.Tasks,因为我正在用C#开发操作系统(编译器是github.com/nifanfa/MOSA-Core)它不支持某些系统dll
  • Elijah629 在这种情况下很不幸,但您最终可能会重新发明 Task 类型,并从头开始实现它。
  • 你能编译IEnumerable生成器,滥用它来创建可恢复的协同程序(类似于统一)作为任务替换吗?

标签: c# multithreading


【解决方案1】:

可以使用一个小的帮助类/帮助线程来限制活动线程的数量,该帮助类/辅助线程管理多少线程同时处于活动状态并像这样使用它

for (var i = 0; i < list.Count; i++)
{
    var iterator = i;//note the iterator in a for loop isn't declared inside it's inner scope and will change on increment, similar also for foreach
    threadHelper.AddAction(() => { /* do stuff */ });

}
/* some more code that dont need the results of the threads*/

threadHelper.WaitForEnd();

之前没有使用纯线程类,之前只对任务做了类似的事情,它可能需要一些剧烈的优化,但我的幼稚方法是这样的

public class ThreadHelper
{
    private Thread[] Threads;
    private Thread CheckThread;
    private Queue<Action> Actions = new Queue<Action>();
    private int UpdateSpeed;
    public int RunnignThreads = 0;
    public bool Completed => RunnignThreads == 0 && !Actions.Any();



       public ThreadHelper(int maxThreads, int updateSpeed)
        {
            Threads = new Thread[maxThreads];
            UpdateSpeed = updateSpeed;
            // small thread to check if new actions should be executed
            CheckThread = new Thread(() =>
            {
                while (true)
                {
                    for (var i = 0; i < Threads.Length; i++)
                    {
                        //remove completed threads
                        if (!(Threads[i]?.IsAlive ?? true))
                        {
                            RunnignThreads--;
                            Threads[i] = null;
                        }
                        //add new
                        lock (Actions)
                        {
                            if (Threads[i] == null && Actions.Any())
                            {
    
                                var threadAction = Actions.Dequeue();
                                Threads[i] = new Thread(() => threadAction());
                                Threads[i].Start();
                                RunnignThreads++;
                            }
    
                        }
    
                    }
                    // just a small delay to 
                    Thread.Sleep(updateSpeed);
                }
            });
            CheckThread.Start();
        }

        ~ThreadHelper()
        {
            CheckThread.Abort();
        }
    
        public void AddAction(Action action)
        {
            lock (Actions)
            {
                Actions.Enqueue(action);
            }
        }
        //Maybe the waiting is a little bit trickier...
        public void WaitForEnd()
        {
            Thread.Sleep(UpdateSpeed *2 ); //maybe no threads are even startet before this gets called
            while (!Completed)
            {
                Thread.Sleep(UpdateSpeed);
            }
        }
    }
 

【讨论】:

  • CheckThread.Abort():.NET 平台不再支持Thread.Abort 方法。它抛出一个PlatformNotSupportedException。
  • 哦,没看到 - 我应该阅读编译器警告 :) 在这种情况下,像取消令牌之类的东西似乎是必要的。有趣的是,他们在 .net 5 中删除了它。它可能很危险,但最近甚至有一个很好的用例,因为它可以与 .net 框架一起使用,我无法控制似乎是很长的调用的代码主要是为它省钱
  • Mucksh AFAIK 在调用需要太长时间或有死锁倾向的外部不可取消 API 时,推荐的方法是在可以卸载的单独 AppDomain 上调用它们,或者在可以杀死的单独进程。
  • 有可能。谢谢将是一个更安全的解决方案
猜你喜欢
  • 2011-07-02
  • 1970-01-01
  • 1970-01-01
  • 2021-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-26
相关资源
最近更新 更多