我正在用 C# 创建一个 Windows 服务。其目的是从 Internet 上的提要中获取信息。我使用 zeromq 的 pub/sub 架构获取数据(我的服务仅是订阅者)。为了调试服务,我将它“托管”在 WPF 控制面板中。这使我无需安装即可启动、运行和停止服务。我看到的问题是,当我调用我的停止方法时,它看起来好像服务继续写入数据库。我知道这一点,因为我Debug.WriteLine()
在写作发生的地方放了一个。
有关该服务的更多信息:
我正在尝试以允许它异步写入数据库的方式构建我的服务。这是通过使用线程和线程池的组合来实现的。
public void StartDataReceiver() // Entry point to service from WPF host
{
// setup zmq subscriber socket
receiverThread = new Tread(SpawnReceivers);
receiverThread.Start();
}
internal void SpawnReceivers()
{
while(!stopEvent.WaitOne(0))
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessReceivedData), subscriber.Recv()); // subscriber.Recv() blocks when there is no data to receive (according to the zmq docs) so this loop should remain under control, and threads only created in the pool when there is data to process.
}
}
internal void ProcessReceivedData(Object recvdData)
{
// cast recvdData from object -> byte[]
// convert byte[] -> JSON string
// deserialize JSON -> MyData
using (MyDataEntities context = new MyDataEntities())
{
// build up EF model object
Debug.WriteLine("Write obj to db...");
context.MyDatas.Add(myEFModel);
context.SaveChanges();
}
}
internal void QData(Object recvdData)
{
Debug.WriteLine("Queued obj in queue...");
q.Enqueue((byte[])recvdData);
}
public void StopDataReceiver()
{
stopEvent.Set();
receiverThread.Join();
subscriber.Dispose();
zmqContext.Dispose();
stopEvent.Reset();
}
上面的代码是我关心的方法。当我调试 WPF 主机并且该方法ProcessReceivedData
设置为在线程池中排队时,一切似乎都按预期工作,直到我通过调用StopDataReceiver
. 据我所知,线程池永远不会再排队任何线程(我通过在该行上放置一个断点来检查这一点),但我继续在输出窗口中看到“Write obj to db ...”,当我' Break All' 在调试器中,context.SaveChanges();
行上会出现一个绿色的小箭头,指示当前停止执行的位置。当我进行更多测试并让线程池排队时,该方法QData
似乎一切正常。我在输出窗口中看到“Queued obj in queue...”消息,直到我停止服务。
TL;博士:
我不知道如何确定实体框架是否只是在减慢速度,而我看到的消息只是线程池正在清除其积压的工作项,或者是否有更大的事情在起作用。我该如何解决这样的问题?
更好的解决方案是像我在QData
方法中那样将传入的 JSON 字符串作为 byte[] 排队,然后让线程池排队使用不同的方法来清除队列。我觉得那个解决方案只会转移问题,而不是真正解决它。
另一种解决方案是编写一个专门用于清除该队列的新服务吗?我在编写另一个服务时看到的问题是我可能不得不使用 WCF(或者可能是 zmq)在两个服务之间进行通信,这显然会增加开销并且可能会降低性能。
我认为所有这一切的关键部分是足够快地从网络中获取数据,因为如果我的订阅者跟不上,我订阅的发布者将开始丢弃消息。