我有以下代码:
public class EmailJobQueue
{
private EmailJobQueue()
{
}
private static readonly object JobsLocker = new object();
private static readonly Queue<EmailJob> Jobs = new Queue<EmailJob>();
private static readonly object ErroredIdsLocker = new object();
private static readonly List<long> ErroredIds = new List<long>();
public static EmailJob GetNextJob()
{
lock (JobsLocker)
{
lock (ErroredIdsLocker)
{
// If there are no jobs or they have all errored then get some new ones - if jobs have previously been skipped then this will re get them
if (!Jobs.Any() || Jobs.All(j => ErroredIds.Contains(j.Id)))
{
var db = new DBDataContext();
foreach (var emailJob in db.Emailing_SelectSend(1))
{
// Dont re add jobs that exist
if (Jobs.All(j => j.Id != emailJob.Id) && !ErroredIds.Contains(emailJob.Id))
{
Jobs.Enqueue(new EmailJob(emailJob));
}
}
}
while (Jobs.Any())
{
var curJob = Jobs.Dequeue();
// Check the job has not previously errored - if they all have then eventually we will exit the loop
if (!ErroredIds.Contains(curJob.Id))
return curJob;
}
return null;
}
}
}
public static void ReInsertErrored(long id)
{
lock (ErroredIdsLocker)
{
ErroredIds.Add(id);
}
}
}
然后我启动10个执行此操作的线程:
var email = EmailJobQueue.GetNextJob();
if (email != null)
{
// Breakpoint here
}
问题在于,如果我在注释所在地放置一个断点并向队列添加一个项目,那么断点会多次被击中。这是我的代码问题还是VS调试器的特性?
谢谢,
乔
答案 0 :(得分:2)
这是调试应用程序的多线程部分的副作用。
您看到每个线程都会遇到断点。调试应用程序的多线程部分很棘手,因为您实际上是在同时调试所有线程。实际上,有时候,当你逐步完成时它会在类之间跳转,因为它在所有这些线程上做了不同的事情,具体取决于你的应用程序。
现在,要解决它是否是线程安全的。这实际上取决于你如何在这些线程上使用资源。如果您只是阅读,那么可能它是线程安全的。但是,如果您正在撰写,则需要至少利用共享对象上的lock
操作:
lock (someLockObject)
{
// perform the write operation
}
答案 1 :(得分:2)
看起来好像是从数据库中获取作业:
foreach (var emailJob in db.Emailing_SelectSend(1))
该数据库调用是否将记录标记为将来查询中的部分不可用?如果没有,我相信这就是你多次达到突破点的原因。
例如,如果我用以下内容替换对数据库的调用,我会看到你的行为。
// MockDB is a static configured as `MockDB.Enqueue(new EmailJob{Id = 1})`
private static IEnumerable<EmailJob> GetJobFromDB()
{
return new List<EmailJob>{MockDB.Peek()};
}
但是,如果我实际从模拟数据库中出队,它只会遇到断点一次。
private static IEnumerable<EmailJob> GetJobFromDB()
{
var list = new List<EmailJob>();
if (MockDB.Any())
list.Add(MockDB.Dequeue());
return list;
}