C# 如何使一定数量的线程始终运行

C# 如何使一定数量的线程始终运行,c#,wpf,multithreading,threadpool,multitasking,C#,Wpf,Multithreading,Threadpool,Multitasking,好的,这是我的问题。我想开始线程,直到达到一定数量。比如说100。因此,它将开始启动线程并持续检查运行线程的数量。 当达到最大数量时,它将停止启动新线程。但是,如果有一个适当的检查间隔或已完成的线程,它将发出信号并启动新线程 通过这种方式,我将始终拥有一定数量的运行线程 我用睡眠和永久性睡眠来解决这个问题。所以我在给定的时间间隔内不断检查运行的线程总数,若线程已完成,则处理它并启动一个新线程 但我的解决方案并没有以正确的方式向我走来。我认为,如果完成的线程发出信号,并且如果我们低于最大线程数阈值

好的,这是我的问题。我想开始线程,直到达到一定数量。比如说100。因此,它将开始启动线程并持续检查运行线程的数量。 当达到最大数量时,它将停止启动新线程。但是,如果有一个适当的检查间隔或已完成的线程,它将发出信号并启动新线程

通过这种方式,我将始终拥有一定数量的运行线程

我用睡眠和永久性睡眠来解决这个问题。所以我在给定的时间间隔内不断检查运行的线程总数,若线程已完成,则处理它并启动一个新线程

但我的解决方案并没有以正确的方式向我走来。我认为,如果完成的线程发出信号,并且如果我们低于最大线程数阈值,那么检查器将启动一个新的线程,这会更好


我看到了许多线程池示例,但其中大多数都不包含任何具有最大运行线程数的排队池。我的意思是,他们只是不断地启动线程,直到完成为止。但假设我有500k个URL要获取。我不能用线程池在for循环中启动所有线程

平台是c#4.5 WPF应用程序

下面是我的解决方案。实际上我在找一个更好的。没有改进这个

private void Button_Click_4(object sender, RoutedEventArgs e)
{
    Task.Factory.StartNew(() =>
    {
        startCrawler();
    });
}

void startCrawler()
{
    int irMaximumThreadcount = 100;
    List<Task> lstStartedThreads = new List<Task>();
    while (true)
    {
        for (int i = 0; i < lstStartedThreads.Count; i++)
        {
            if (lstStartedThreads[i].IsCompleted == true)
            {
                lstStartedThreads[i].Dispose();
                lstStartedThreads.RemoveAt(i);
            }
        }

        if (lstStartedThreads.Count < irMaximumThreadcount)
        {
            var vrTask = Task.Factory.StartNew(() =>
            {
                func_myTask();
            });
            lstStartedThreads.Add(vrTask);
        }

        System.Threading.Thread.Sleep(50);
    }
}

void func_myTask()
{

}
private void按钮\u单击\u 4(对象发送者,路由目标)
{
Task.Factory.StartNew(()=>
{
startCrawler();
});
}
void startCrawler()
{
int irMaximumThreadcount=100;
List lstStartedThreads=新列表();
while(true)
{
对于(int i=0;i
{
func_myTask();
});
lstStartedThreads.Add(vrTask);
}
系统.线程.线程.睡眠(50);
}
}
void func_myTask()
{
}
我个人会使用此方法,特别是将并发执行次数限制为传入值的方法

private IEnumerable<Action> InfiniteFunctions()
{
    while(true)
    {
        yield return func_myTask;
    }
}

private void Button_Click_4(object sender, RoutedEventArgs e)
{
    int irMaximumThreadcount = 100;
    InfiniteFunctions()
        .AsParallel()
        .WithDegreeOfParallelism(irMaximumThreadcount)
        .ForAll(f => f());
}

这不是一个确切的答案,但我认为这可能会指引你走向正确的方向

首先,看一看,特别是本页底部给出的简单示例。这种方法优于Thread.Sleep(),更适合您的目的。我在考虑加入“管理者”线程,而不是“睡眠”线程


第二个选项可能适合您的目的,也可能不适合您的目的,即新的
任务
库。因为您使用的是最新版本的框架,所以这个选项是可用的,但是我猜您无法控制任务库创建的线程的实际数量。它会根据底层调度程序自动选择该值。但是,有一个名为的选项听起来很有趣。

.NET4.0引入了几个具有内置并发管理的集合,这些集合应该非常适合这种情况。阻塞收集将比在while循环中睡眠更有效。然后,您只需要生成x个线程,这些线程从阻塞队列中读取数据

BlockingCollection<string> queue = new BlockingCollection<string>(listOfUrls);

for (int x=0; x < MaxThreads; x++)
{
    Task.Factory.StartNew(() => 
    {
        while (true)
        {
            string url = queue.Take(); // blocks until url is available
            // process url;
        }
    }, TaskCreationOptions.LongRunning);
}
BlockingCollection队列=新的BlockingCollection(listOfUrls);
对于(int x=0;x
{
while(true)
{
string url=queue.Take();//阻塞,直到url可用为止
//处理url;
}
},TaskCreationOptions.LongRunning);
}

您将任务标记为长时间运行,以便它将创建自己的线程,而不是使用线程池。如果需要先进先出,可以将
ConcurrentQueue
传递给阻塞集合构造函数

您将任务与线程混为一谈。任务不是线程

实际上,TPL()是一种队列。这意味着您可以为您拥有的每个
Func
Action
对象创建并启动任务。它们实际上是被创造出来的

但是,您可以创建许多任务,开销很小,因为TPL将使它们排队,并应用进一步的逻辑来平衡任务线程上的工作

如果某些任务需要一个接一个地执行,您可以使用将其排队。也可以使用或启动新任务

这也是您如何控制要创建的并行任务数量的线索:只需创建所需数量的任务,并在任何情况下使用
continuewheny
将剩余任务排队。每次任务结束时,将启动下一个任务


同样:TPL将平衡线程池中线程之间的工作。您需要考虑的是使用其他资源,如磁盘I/O或Internet连接。有许多任务试图同时使用相同的资源,这会大大降低程序的速度。

您可以管理自己的任务/线程池,等待任何线程完成,然后立即启动一个新的线程

MAX_THREAD_ALLOWED = 100;
List<Task> tasks = new List<Task>();
for (int i = 0; i < 1000; i++)
{
    tasks.Add(Task.Run(() => { Foo(i); }));
    if (i == MAX_THREAD_ALLOWED)
    {
        Task.WaitAny(tasks.ToArray());
        MAX_THREAD_ALLOWED++;
    }
}
MAX\u THREAD\u ALLOWED=100;
列表任务=新列表();
对于(int i=0;i<1000;i++)
{
tasks.Add(Task.Run(()=>{Foo(i);}));
如果(i==允许的最大线程数)
{
Task.WaitAny(tasks.ToArray());
允许的最大线程数++;
}
}

据我所知,线程联接用于等待所有任务完成。我错了吗?如果是,我如何使用它?我不需要等待所有的任务。当一个任务完成时,另一个任务将立即启动,因此始终会有一定数量的任务运行mmmm…不是100%确定,但我认为Join只会停止调用线程。另一个想法是加入新创建的工作线程,以便在当前运行的线程之一发出完成的信号时,它们立即开始工作,这样管理器就不必反复检查。不,这不起作用。因为线程是独立的
MAX_THREAD_ALLOWED = 100;
List<Task> tasks = new List<Task>();
for (int i = 0; i < 1000; i++)
{
    tasks.Add(Task.Run(() => { Foo(i); }));
    if (i == MAX_THREAD_ALLOWED)
    {
        Task.WaitAny(tasks.ToArray());
        MAX_THREAD_ALLOWED++;
    }
}