我需要逐个处理用户请求(类似于队列作业)
这就是我所拥有的:
Thread checkQueue;
Boolean IsComplete = true;
protected void Start()
{
checkQueue = new Thread(CheckQueueState);
checkQueue.Start();
}
private void CheckQueueState()
{
while (true)
{
if (checkIsComplete)
{
ContinueDoSomething();
checkQueue.Abort();
break;
}
System.Threading.Thread.Sleep(1000);
}
}
protected void ContinueDoSomething()
{
IsComplete = false;
...
...
IsComplete = true; //when done, set it to true
}
每当有来自用户的新请求时,系统将调用Start()
函数并检查上一个作业是否完成,如果是,则继续下一个作业。
但我不确定以这种方式做是否正确。
有任何改进或更好的方法吗?
答案 0 :(得分:1)
我喜欢usr关于使用TPL Dataflow的建议。如果您能够将外部依赖项添加到项目中(TPL Dataflow不作为.NET框架的一部分分发),那么它将为您的问题提供一个干净的解决方案。
但是,如果你坚持使用框架所提供的内容,那么你应该看一下BlockingCollection<T>
,它可以很好地处理你试图实现的生产者 - 消费者模式。
我将一个快速的.NET 4.0示例放在一起,以说明如何在您的场景中使用它。它不是很精简,因为它有很多调用Console.WriteLine()
。但是,如果你把所有杂乱的东西都搞得一团糟,那就太简单了。
它的中心是BlockingCollection<Action>
,它从任何线程中添加了Action
个委托,以及一个专门用于在{0}中顺序出列并执行这些Action
的线程。它们的确切顺序。
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
namespace SimpleProducerConsumer
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main thread id is {0}.", Thread.CurrentThread.ManagedThreadId);
using (var blockingCollection = new BlockingCollection<Action>())
{
// Start our processing loop.
var actionLoop = new Thread(() =>
{
Console.WriteLine(
"Starting action loop on thread {0} (dedicated action loop thread).",
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread);
// Dequeue actions as they become available.
foreach (var action in blockingCollection.GetConsumingEnumerable())
{
// Invoke the action synchronously
// on the "actionLoop" thread.
action();
}
Console.WriteLine("Action loop terminating.");
});
actionLoop.Start();
// Enqueue some work.
Console.WriteLine("Enqueueing action 1 from thread {0} (main thread).", Thread.CurrentThread.ManagedThreadId);
blockingCollection.Add(() => SimulateWork(1));
Console.WriteLine("Enqueueing action 2 from thread {0} (main thread).", Thread.CurrentThread.ManagedThreadId);
blockingCollection.Add(() => SimulateWork(2));
// Let's enqueue it from another thread just for fun.
var enqueueTask = Task.Factory.StartNew(() =>
{
Console.WriteLine(
"Enqueueing action 3 from thread {0} (task executing on a thread pool thread).",
Thread.CurrentThread.ManagedThreadId);
blockingCollection.Add(() => SimulateWork(3));
});
// We have to wait for the task to complete
// because otherwise we'll end up calling
// CompleteAdding before our background task
// has had the chance to enqueue action #3.
enqueueTask.Wait();
// Tell our loop (and, consequently, the "actionLoop" thread)
// to terminate when it's done processing pending actions.
blockingCollection.CompleteAdding();
Console.WriteLine("Done enqueueing work. Waiting for the loop to complete.");
// Block until the "actionLoop" thread terminates.
actionLoop.Join();
Console.WriteLine("Done. Press Enter to quit.");
Console.ReadLine();
}
}
private static void SimulateWork(int actionNo)
{
Thread.Sleep(500);
Console.WriteLine("Finished processing action {0} on thread {1} (dedicated action loop thread).", actionNo, Thread.CurrentThread.ManagedThreadId);
}
}
}
输出是:
0.016s: Main thread id is 10.
0.025s: Enqueueing action 1 from thread 10 (main thread).
0.026s: Enqueueing action 2 from thread 10 (main thread).
0.027s: Starting action loop on thread 11 (dedicated action loop thread).
0.028s: Enqueueing action 3 from thread 6 (task executing on a thread pool thread).
0.028s: Done enqueueing work. Waiting for the loop to complete.
0.527s: Finished processing action 1 on thread 11 (dedicated action loop thread).
1.028s: Finished processing action 2 on thread 11 (dedicated action loop thread).
1.529s: Finished processing action 3 on thread 11 (dedicated action loop thread).
1.530s: Action loop terminating.
1.532s: Done. Press Enter to quit.
答案 1 :(得分:0)
使用TPL Dataflow库中的ActionBlock<T>
。将其MaxDegreeOfParalellism设置为1并完成。
请注意,ASP.NET工作进程可以随时回收(例如,由于计划的回收,内存限制,服务器重新启动或部署),因此排队的工作可能会突然丢失,恕不另行通知。我建议您查看一些外部排队解决方案,如MSMQ(或其他),以获得可靠的队列。
答案 2 :(得分:0)
查看Microsoft的Reactive Extensions。该库包含一组可用的调度程序,它们遵循您需要的语义。
最适合您需求的是EventLoopScheduler
。它将排队行动并一个接一个地执行它们。如果它完成一个动作并且队列中有更多项,它将按顺序处理同一线程上的动作,直到队列为空,然后它处理该线程。当新操作排队时,它会创建一个新线程。因此,它非常有效。
代码非常简单,如下所示:
var scheduler = new System.Reactive.Concurrency.EventLoopScheduler();
scheduler.Schedule(() => { /* action here */ });
如果您需要在新线程上执行每个排队操作,请使用它:
var scheduler = new System.Reactive.Concurrency.NewThreadScheduler();
scheduler.Schedule(() => { /* action here */ });
很简单。