我有一个Windows服务来处理链表队列中的xml文件。创建文件时,FileSystemWatcher事件添加了队列中的文件。
namespace XMLFTP
{
public class XML_Processor : ServiceBase
{
public string s_folder { get; set; }
public XML_Processor(string folder)
{
s_folder = folder;
}
Thread worker;
FileSystemWatcher watcher;
DirectoryInfo my_Folder;
public static AutoResetEvent ResetEvent { get; set; }
bool running;
public bool Start()
{
my_Folder = new DirectoryInfo(s_folder);
bool success = true;
running = true;
worker = new Thread(new ThreadStart(ServiceLoop));
worker.Start();
// add files to queue by FileSystemWatcher event
return (success);
}
public bool Stop()
{
try
{
running = false;
watcher.EnableRaisingEvents = false;
worker.Join(ServiceSettings.ThreadJoinTimeOut);
}
catch (Exception ex)
{
return (false);
}
return (true);
}
public void ServiceLoop()
{
string fileName;
while (running)
{
Thread.Sleep(2000);
if (ProcessingQueue.Count > 0)
{
// process file and write info to DB.
}
}
}
void watcher_Created(object sender, FileSystemEventArgs e)
{
switch (e.ChangeType)
{
case WatcherChangeTypes.Created:// add files to queue
}
}
}
}
可能存在线程安全问题。
while (running)
{
Thread.Sleep(2000);
if (ProcessingQueue.Count > 0)
{
// process file and write info to DB.
}
}
由于对ProcessingQueue.Count的访问不受锁保护,如果另一个线程改变"队列",则Count可以改变。结果,进程文件部分可能会失败。如果您将Count属性实现为:
,情况也是如此public static int Count
{
get { lock (syncRoot) return _files.Count; }
}
因为锁被释放到了早期。
我的两个问题:
如果我使用.NET Framework 4.5 BlockingCollection技能,示例代码为:
class ConsumingEnumerableDemo
{
// Demonstrates:
// BlockingCollection<T>.Add()
// BlockingCollection<T>.CompleteAdding()
// BlockingCollection<T>.GetConsumingEnumerable()
public static void BC_GetConsumingEnumerable()
{
using (BlockingCollection<int> bc = new BlockingCollection<int>())
{
// Kick off a producer task
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 10; i++)
{
bc.Add(i);
Thread.Sleep(100); // sleep 100 ms between adds
}
// Need to do this to keep foreach below from hanging
bc.CompleteAdding();
});
// Now consume the blocking collection with foreach.
// Use bc.GetConsumingEnumerable() instead of just bc because the
// former will block waiting for completion and the latter will
// simply take a snapshot of the current state of the underlying collection.
foreach (var item in bc.GetConsumingEnumerable())
{
Console.WriteLine(item);
}
}
}
}
该示例使用常量10作为iteration-clause,如何将队列中的动态计数应用于它?
答案 0 :(得分:3)
使用BlockingCollection
,您无需知道计数。消费者知道在队列为空并且IsCompleted
为真之前保持处理项目。所以你可以这样:
var producer = Task.Factory.StartNew(() =>
{
// Add 10 items to the queue
foreach (var i in Enumerable.Range(0, 10))
queue.Add(i);
// Wait one minute
Thread.Sleep(TimeSpan.FromMinutes(1.0));
// Add 10 more items to the queue
foreach (var i in Enumerable.Range(10, 10))
queue.Add(i);
// mark the queue as complete for adding
queue.CompleteAdding();
});
// consumer
foreach (var item in queue.GetConsumingEnumerable())
{
Console.WriteLine(item);
}
消费者将输出前10个项目,这会清空队列。但由于生产者没有调用CompleteAdding
,消费者将继续阻止队列。它将捕获生产者写下的10个项目。然后,队列为空并且IsCompleted == true
,因此消费者结束(GetConsumingEnumerable
到达队列的末尾)。
您可以随时查看Count
,但您获得的价值只是快照。在您评估它时,生产者或消费者可能会修改队列并更改计数。但这应该不重要。只要您不拨打CompleteAdding
,消费者就会继续等待某个项目。
生产者编写的项目数不必是常数。例如,在我的Simple Multithreading博文中,我展示了一个生产者,它读取文件并将项目写入由消费者提供服务的BlockingCollection
。生产者和消费者同时运行,一切都进行到生产者到达文件末尾。