1

我想创建一个 LINQ to SQL 支持的队列。但是我不确定最好的方法是什么。

现在我做了这样的事情:

public static void Queue(Item item)
{
    var db = new MyDataContext();

    item.Time = DateTime.Now;

    db.Items.InsertOnSubmit(item);

    db.SubmitChanges();
}

public static Item TryDequeue()
{
    try
    {
        var db = new MyDataContext();

        var item = db.Items
            .Where(x => x.Status == 0)
            .OrderBy(x => x.Time)
            .FirstOrDefault();

        if (item == null)
            return null;

        item.Status += 1;

        db.SubmitChanges();

        return item;
    }
    catch (ChangeConflictException)
    {
        return null;
    }
}

但是,我确实得到了一些ChangeConflictExceptions。

我希望得到的查询是一个原子事务,它选择元素并设置它的状态,然后返回,没有任何冲突,我认为情况似乎并非如此。我试过使用TransactionScope,但它抱怨死锁。

实现这一目标的最佳方法是什么?

4

3 回答 3

2

您可以通过这样做继续处理并发问题

db.SubmitChanges(ConflictMode.ContinueOnConflict);

但请理解它的陷阱

于 2012-11-01T10:36:00.527 回答
1

这听起来像您TryDequeue同时在多个线程上运行。

您是否考虑过将队列管理留给单个主线程,然后当它们可用时将工作交给其他人?您总是会遇到多个线程更新相同数据库记录的问题。

即使您强制事务中的所有内容,线程也会开始阻塞,等待其他线程检查工作队列(并且数据库操作在多线程方面很)。这将很快导致瓶颈。

另一种方法是在线程安全集合中使用内存缓存。队列项目将被加载到集合中,线程可以从那里出列。每隔一段时间(或当队列为空时),通过该方法的第一个线程可能会阻塞,将缓存中的更改刷新回数据库,加载新队列,然后正常出列并停止阻塞。这大大减少了您的数据库访问次数,并且应该避免冲突问题,因为在任何给定时间只有一个线程将执行更新。当然,不利的一面是,现在在更新作业的状态和存储在数据库中的更改之间存在延迟——这取决于这会有多大的问题?

The quick 'n dirty fix which will resolve the current error but not the underlying problem would be to lock and so limit threads moving through your Dequeue method...

private Object dequeueLock = new Object();
public static Item TryDequeue()
{
    lock (dequeueLock)
    {
        try
        {
            var db = new MyDataContext();

            var item = db.Items
                .Where(x => x.Status == 0)
                .OrderBy(x => x.Time)
                .FirstOrDefault();

            if (item == null)
                return null;

            item.Status += 1;

            db.SubmitChanges();

            return item;
        }
        catch (ChangeConflictException)
        {
            return null;
        }
    }
}
于 2012-11-01T11:14:39.543 回答
-1

试试这个...

private static readonly object _lock = new object

public static void Queue(Item item)
{
    try
    {
        Monitor.Enter(_lock);
        var db = new MyDataContext();

        item.Time = DateTime.Now;

        db.Items.InsertOnSubmit(item);

        db.SubmitChanges();
    }
    finally
    {
        Monitor.Exit(_lock);
    }
}

public static Item TryDequeue()
{
    try
    {
        Monitor.Enter(_lock);
        var db = new MyDataContext();

        var item = db.Items
            .Where(x => x.Status == 0)
            .OrderBy(x => x.Time)
            .FirstOrDefault();

        if (item == null)
            return null;

        item.Status += 1;

        db.SubmitChanges();

        return item;
    }
    catch (ChangeConflictException)
    {
        return null;
    }
    finally
    {
        Monitor.Exit(_lock);
    }
}
于 2012-11-01T10:25:31.233 回答