有一个队列。有一个函数处理来自此队列的消息。此函数从队列中获取消息,启动新任务以处理下一条消息,等待来自其他源的数据,然后执行计算。
这是示例
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TestTaskFactory
{
class Program
{
static int Data = 50;
static int ActiveTasksNumber = 0;
static int MaxActiveTasksNumber = 0;
static Stopwatch clock = new Stopwatch();
static object locker = new object();
static object locker2 = new object();
static void Main(string[] args)
{
clock.Start();
Task.Factory.StartNew(() => DoWork());
while (true)
{
Thread.Sleep(10000);
}
}
public static void DoWork()
{
//imitation of geting message from some queue
int message = GetMessageFromQueue();
lock (locker2)
{
ActiveTasksNumber++;
MaxActiveTasksNumber = Math.Max(MaxActiveTasksNumber,
ActiveTasksNumber);
Console.Write("\r" + message + " ");
}
//Run new task to work with next message
Task.Factory.StartNew(() => DoWork());
//imitation wait some other data
Thread.Sleep(3000);
//imitation of calculations with message
int tmp = 0;
for (int i = 0; i < 30000000; i++)
{
tmp = Math.Max(message, i);
}
lock (locker2)
{
ActiveTasksNumber--;
}
}
public static int GetMessageFromQueue()
{
lock (locker)
{
if (Data == 0)
{
//Queue is empty. All tasks completed except one
//that is waiting for new data
clock.Stop();
Console.WriteLine("\rMax active tasks number = "
+ MaxActiveTasksNumber
+ "\tTime = " + clock.ElapsedMilliseconds + "ms");
Console.Write("Press key to run next iteration");
clock.Reset();
Console.ReadKey();
Console.Write(" ");
//In queue received new data. Processing repeat
clock.Start();
ActiveTasksNumber = 0;
MaxActiveTasksNumber = 0;
Data = 50;
}
Data--;
return Data;
}
}
}
}
我的猜测是,当队列为空时,除了一个等待新数据的任务外,所有任务都已完成。当数据到达队列时,重复计算。
但是如果你看结果,每次同时运行的任务数量都会增加。
为什么会这样?
测试结果
答案 0 :(得分:3)
你的做法是错误的。
首先,你的队列在哪里? 对于要在并发环境中排队的任何作业,请使用ConcurrentQueue。
并发队列以这种方式使用,不需要随时锁定。
// To create your Queue
ConcurrentQueue<string> queue = new ConcurrentQueue<string>();
// To add objects to your Queue
queue.Enqueue("foo");
// To deque items from your Queue
String bar;
queue.TryDequeue(out bar);
// To loop a process until your Queue is empty
while(!queue.IsEmpty)
{
String bar;
queue.TryDequeue(out bar);
}
接下来是如何递增和递减计数器,有一种更好的方法,它是线程安全的。同样,数据不需要锁定。
// Change your data type from int to long
static long ActiveTasksNumber = 0;
static long MaxActiveTasksNumber = 0;
// To increment the values in a Thread safe fashion:
Interlocked.Increment(ref ActiveTasksNumber);
// To decrement:
Interlocked.Decrement(ref MaxActiveTasksNumber);
实施我向您展示的内容,它应该让您的问题消失
编辑: 命名空间
using System.Collections.Concurrent;
using System.Threading;
答案 1 :(得分:3)
扩展我的评论:
从本质上讲,你有这个:
public static void DoWork()
{
// imitation of geting message from some queue
int message = GetMessageFromQueue();
// Run new task to work with next message
Task.Factory.StartNew(() => DoWork());
// do some work
}
您的代码将获取第一条消息,启动任务以处理下一个项目,然后执行其工作。当第一个任务正在运行时,第二个任务获取一个项目并产生另一个任务以从队列中获取项目。所以现在你有两个线程据说正在做工作,第三个线程会产生另一个线程,等等。 。
代码中没有任何内容阻止它为队列中的每个项创建新任务。
如果你的队列开始时有38件事情,你最有可能最终得到38个并发任务。
您需要同时限制正在运行的任务数量。有很多方法可以做到这一点。也许最简单的是使用BlockingCollection的简单生产者 - 消费者模型。