这个线程安全吗?断点多次被击中

时间:2013-08-22 20:01:37

标签: c# multithreading

我有以下代码:

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调试器的特性?

谢谢,

2 个答案:

答案 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;
}