死锁问题

时间:2010-04-28 08:51:02

标签: c# com-interop deadlock

你好,我遇到了以下代码的死锁问题。当我调用函数getMap()时会发生这种情况。但我无法重新看到可能导致这种情况的原因。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;

using AForge;
using AForge.Imaging;
using AForge.Imaging.Filters;
using AForge.Imaging.Textures;
using AForge.Math.Geometry;

namespace CDIO.Library
{
    public class Polygon
    {
        List<IntPoint> hull;
        public Polygon(List<IntPoint> hull)
        {
            this.hull = hull;
        }

        public bool inPoly(int x, int y)
        {
            int i, j = hull.Count - 1;
            bool oddNodes = false;

            for (i = 0; i < hull.Count; i++)
            {
                if (hull[i].Y < y && hull[j].Y >= y
                || hull[j].Y < y && hull[i].Y >= y)
                {
                    try
                    {
                        if (hull[i].X + (y - hull[i].X) / (hull[j].X - hull[i].X) * (hull[j].X - hull[i].X) < x)
                        {
                            oddNodes = !oddNodes;
                        }
                    }
                    catch (DivideByZeroException e)
                    {
                        if (0 < x)
                        {
                            oddNodes = !oddNodes;
                        }
                    }
                }
                j = i;
            }
            return oddNodes;
        }

        public Rectangle getRectangle()
        {
            int x = -1, y = -1, width = -1, height = -1;
            foreach (IntPoint item in hull)
            {
                if (item.X < x || x == -1)
                    x = item.X;
                if (item.Y < y || y == -1)
                    y = item.Y;


                if (item.X > width || width == -1)
                    width = item.X;
                if (item.Y > height || height == -1)
                    height = item.Y;


            }
            return new Rectangle(x, y, width-x, height-y);
        }

        public Point[] getMap()
        {
            List<Point> points = new List<Point>();
            lock (hull)
            {
                Rectangle rect = getRectangle();
                for (int x = rect.X; x <= rect.X + rect.Width; x++)
                {
                    for (int y = rect.Y; y <= rect.Y + rect.Height; y++)
                    {
                        if (inPoly(x, y))
                            points.Add(new Point(x, y));
                    }
                }
            }
            return points.ToArray();
        }

        public float calculateArea()
        {
            List<IntPoint> list = new List<IntPoint>();
            list.AddRange(hull);
            list.Add(hull[0]);

            float area = 0.0f;
            for (int i = 0; i < hull.Count; i++)
            {
                area += list[i].X * list[i + 1].Y - list[i].Y * list[i + 1].X;
            }
            area = area / 2;
            if (area < 0)
                area = area * -1;
            return area;
        }
    }
}

编辑: “使用System.Threading;”只是为了一些调试ealyer我们让thead睡了一下,我只是忘了删除它。

我们添加了“锁(船体)”以查看它是否可以修复死锁,它不会。 此程序也不会被多线程破坏,所以这不是问题。

我把它缩小到

中的错误
if (inPoly(x, y))
    points.Add(new Point(x, y));

错误消息

  

CLR无法过渡   从COM上下文0x1bb7b6b0到COM   上下文0x1bb7b900持续60秒。该   拥有目的地的线程   上下文/公寓是最有可能的   或者做一次非抽水等待或者   处理很长时间   没有泵Windows的操作   消息。这种情况一般都有   负面的业绩影响和可能   甚至导致应用程序成为   无响应或内存使用   随着时间的推移不断积累。至   一切都避免这个问题   线程公寓(STA)线程   应该使用抽等待原语   (例如CoWaitForMultipleHandles)和   经常在长时间内发送消息   正在运行。

3 个答案:

答案 0 :(得分:4)

这是一个托管调试助手警告,与在线程上使用COM服务器有关。 COM的一个特性是它可以自动处理不支持多线程的组件的线程。它会自动将后台线程中的方法调用封送到UI线程,以便不以线程不安全的方式使用该组件。这是完全自动的,你不要自己编写任何代码来实现这一点。

为了使其正常工作,UI线程必须处于空闲状态,以便它可以执行方法调用。警告告诉您,UI线程已空闲一分钟,它会阻止呼叫完成。最可能的原因是UI线程阻塞,等待线程完成。这永远不会发生,它已陷入僵局。或者它可能只是忙于运行该分钟的代码,从来没有绕过正常的职责,抽取消息循环。不抽取消息循环可防止编组呼叫完成并跳过警告。

应该很容易看到,应该冻结应用程序的主窗口并在标题栏中显示“无响应”消息。当您使用Debug + Break All,Debug + Windows + Threads并切换到UI线程,然后查看调用堆栈,您应该看到UI线程死锁的位置。通过不让UI线程在线程上等待或避免在工作线程上使用COM组件来修复它。如果它完全不合适(不应该),那么您可以使用Debug + Exceptions关闭警告。

这是警告的技术解释。无聊的是,Visual Studio 2005的RTM版本中存在一个错误。调试器出了问题,它往往在单步执行或检查变量时跳过MDA。已在Service Pack 1中修复,如果尚未执行此操作,请务必下载并安装它。

答案 1 :(得分:1)

在这个msdn article中,他们解释了为什么最好定义一个仅在lock语句中使用的变量。显然,它避免了很多这类问题。

答案 2 :(得分:0)

您在getMap()中锁定实例“hull”,然后在调用getRectangle()时锁定;你试图通过“船体”进行枚举。