为什么我的线程会变成死锁的C#?

时间:2014-06-04 19:04:37

标签: c# multithreading threadpool

我在我正在处理的应用程序中遇到多线程问题。

该过程基本上涉及需要处理的项目列表。作为此处理的一部分,需要对不支持多线程的第三方api进行调用。

我试图引入API类的单例实例并使用锁定来确保只有一个线程一次调用它但我仍然遇到一个情况是一个线程在调用API时卡住了其他人则等待锁定等待释放。

如果我暂停调试会话并检查线程的callstack,那么进入API调用的那个会有以下跟踪:

mscorlib.dll!System.Threading.WaitHandle.WaitAll(System.Threading.WaitHandle [] waitHandles,int millisecondsTimeout,bool exitContext)
                                                                                                mscorlib.dll!System.Threading.WaitHandle.WaitAll(System.Threading.WaitHandle [] waitHandles)

我已经在单个线程上通过在foreach循环中交换线程池并使用对Process方法的显式调用来测试它并且它工作正常(虽然比我想要的慢,但是之前有很多处理并在API调用之后)。

我在这里做错了什么,或者这是第三方api的问题吗?

  public class MyClass
  {
     private static ThirdPartyApi ApiInstance;
     private static object lockObject = new object();

     ...

     public void DoWork(list)
     {
        ...
        foreach (var item in list)
        {
            ThreadPool.QueueUserWorkItem(Process, item);
        }   
        ...
      }


        public void Process(string item)
        {
             // Various processing
              ...
              lock(lockObject)
              {
                  var result = ApiInstance.Lookup(item);
              }
              ...
         }

1 个答案:

答案 0 :(得分:0)

线程不安全的代码并不一定意味着这些方法不是可重入的,一些线程不安全的库要求所有调用都来自同一个线程,句点。请尝试使用BlockingCollection的以下方法,它将在同一个线程上发出所有调用,看看它是否解决了问题。

public class MyClass<T>
{
    private BlockingCollection<T> workQueue = new BlockingCollection<T>();

    public MyClass()
    {
        Task.Factory.StartNew(ProcessWorkQueue, TaskCreationOptions.LongRunning);
    }

    public void DoWork(List<T> work)
    {
        foreach (var workItem in work)
        {
            workQueue.Add(workItem);
        }
    }

    public void StopWork()
    {
        workQueue.CompleteAdding();
    }

    public void ProcessWorkQueue()
    {
        foreach(var item in workQueue.GetConsumingEnumerable())
        {
            //Do something here
        }
    }
}

此外,ThreadPool是一个共享资源,并且在Threadpool线程上执行任何阻止操作都会耗尽它。即使您的代码确实有效,也需要对其进行重构以解决此资源饥饿问题。