线程可以跳过lock()吗?

时间:2014-07-29 22:22:31

标签: c# .net multithreading thread-safety locking

我有一个class,提供对LinkedList<>的线程安全访问(添加和阅读项目)。

class LinkedListManager {
    public static object locker = new object();
    public static LinkedList<AddXmlNodeArgs> tasks { get; set; }
    public static EventWaitHandle wh { get; set; }

    public void AddItemThreadSafe(AddXmlNodeArgs task) {
        lock (locker)
            tasks.AddLast(task);
        wh.Set();
    }

    public LinkedListNode<AddXmlNodeArgs> GetNextItemThreadSafe(LinkedListNode<AddXmlNodeArgs> prevItem) {
        LinkedListNode<AddXmlNodeArgs> nextItem;
        if (prevItem == null) {
            lock (locker)
                return tasks.First;
        }
        lock (locker) // *1
            nextItem = prevItem.Next;
        if (nextItem == null) { // *2
            wh.WaitOne();
            return prevItem.Next;
        }
        lock (locker)
            return nextItem;
        }
    }
}

我有3个主题:第1个 - 将数据写入tasks;第二和第三 - 从tasks读取数据。

在第2和第3个主题中,我通过调用tasksGetNextItemThreadSafe()检索数据。

问题,当方法(GetNextItemThreadSafe())的参数不为空时,有时null会返回prevItem

问题:

某个帖子可以某种方式跳过lock(locker)// *1)并立即转到// *2吗?

我认为这是从null获得返回值= GetNextItemThreadSafe()的唯一方法...

我花了一整天才发现错误,但这非常困难,因为它似乎几乎不可能一步一步地调试它(tasks包含5.000个元素并且发生错误无论何时它想要)。顺便说一下,有时程序运行正常 - 毫无例外。

我是线程新手,所以也许我会问愚蠢的问题......

3 个答案:

答案 0 :(得分:0)

lock仅在执行锁定后声明的代码块时才有效。由于您在单个命令上多次锁定,因此这有效地退化为仅锁定lock之后的单个命令,之后另一个线程可以自由跳入并使用数据。也许你的意思是:

public LinkedListNode<AddXmlNodeArgs> GetNextItemThreadSafe(LinkedListNode<AddXmlNodeArgs> prevItem) {
    LinkedListNode<AddXmlNodeArgs> nextItem;
    LinkedListNode<AddXmlNodeArgs> returnItem;

    lock(locker) { // Lock the entire method contents to make it atomic
      if (prevItem == null) {
        returnItem = tasks.First;
      }

      // *1
      nextItem = prevItem.Next;

      if (nextItem == null) { // *2
        // wh.WaitOne(); // Waiting in a locked block is not a good idea
        returnItem = prevItem.Next;
      }

      returnItem = nextItem;
    }

    return returnItem;
  }
}

请注意,在锁定的块中只发生分配(而不是返回),并且在方法的底部有一个返回点。

答案 1 :(得分:0)

不清楚你想要实现的目标。这两个线程是否应该获得链表的相同元素?或者您是否尝试让2个线程并行处理列表中的任务?如果这是第二种情况,那么你正在做的事情是行不通的。你最好看看BlockingCollection,它是线程安全的,专为这种多线程生产者/消费者模式而设计。

答案 2 :(得分:0)

我认为解决方案如下:

在您的添加方法中,添加节点将EventWaitHandle 设置为在同一锁内

在Get方法中,在锁定内部,检查同一个锁中的下一个元素是否为空,重置EventWaitHandle。在锁定之外,等待EventWaitHandle。