3

我有以下代码:

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

谢谢,

4

2 回答 2

2

这是调试应用程序的多线程部分的副作用。

您会看到在每个线程上都遇到了断点。调试应用程序的多线程部分很棘手,因为您实际上是在同时调试所有线程。事实上,有时,它会在您单步执行时在类之间跳转,因为它在所有这些线程上执行不同的操作,具体取决于您的应用程序。

现在,要解决它是否是线程安全的。这实际上取决于您如何使用这些线程上的资源。如果您只是在阅读,它很可能是线程安全的。但是,如果您正在编写,则至少需要利用lock共享对象的操作:

lock (someLockObject)
{
    // perform the write operation
}
于 2013-08-22T20:03:37.877 回答
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;
}
于 2013-08-22T20:43:10.380 回答