LINQ to SQL - Queue

若如初见. 提交于 2019-12-11 06:15:45

问题


I would like to create a LINQ to SQL backed queue. However I am unsure how the best way to go about it is.

Right now I've done it something like this:

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

However, I do get some ChangeConflictExceptions.

I was hoping the resulting query would be an atomic transaction that picks the element and sets it status and then returns, without any conflicts, thought that doesn't seem to be the case. I've tried using TransactionScope but it complains about deadlocks.

What is the best way to go about achieving this?


回答1:


You could continue with the concurrency issue by doing this

db.SubmitChanges(ConflictMode.ContinueOnConflict);

But please understand its pitfalls.




回答2:


This sounds like you're running TryDequeue on multiple threads concurrently.

Have you considered leaving queue management to a single master thread which then hands work out to others as/when they become available? You're always going to have problems with multiple threads updating the same database records.

Even if you forced everything inside a transaction, threads would start blocking, waiting for other threads to check the work queue (and a db operation is slow in terms of multithreading). This would quickly result in a bottleneck.

An alternative would be to have an in-memory cache in a thread-safe collection. Queue items would be loaded into the collection and threads could dequeue from there. Every so often (or when the queue is empty), the first thread through the method could block, flush the changes in the cache back to the database, load a new queue and then dequeue as normal and stop blocking. This reduces your number of database trips considerably and should avoid the conflict issues since only a single thread will perform the updates at any given time. The downside, of course, is that there's now a delay between the state of a job being updated and that change being stored in the database - It depends how much of an issue this would be?

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



回答3:


Try this...

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);
    }
}


来源:https://stackoverflow.com/questions/13174867/linq-to-sql-queue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!