2

Webアプリのwin32apiを使用しているコードがあります。ASP.Net開発サーバーでこのコードを実行するとデッドロックが発生します(IISで再現することはできませんが、特定のシナリオで発生しないという事実はわかりません)。以下は、私がトリミングしたクラスで、まだ問題を再現しています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.InteropServices;
namespace Web_ShellIconBug
{
    public class IconIndexClass
    {
        [StructLayout(LayoutKind.Sequential)]
        private struct SHFILEINFO
        {
            public IntPtr hIcon;
            public int iIcon;
            public int dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            public string szTypeName;
        }

        [DllImport("shell32", CharSet = CharSet.Unicode)]
        private static extern IntPtr SHGetFileInfo(string pszPath, int dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags);

        private static object m_lock = new object();

        public int IconIndex(
            string fileName,
            bool tryDisk,
            int iconState
            )
        {
            // On some machines, you might need this to make sure multiple threads are spawned
            //System.Threading.Thread.Sleep(100);
            SHFILEINFO shfi = new SHFILEINFO();
            IntPtr retVal;
            uint shfiSize = (uint)Marshal.SizeOf(shfi.GetType());

            MyLog("Before Lock.");
            lock (m_lock)
            {
                MyLog("Obtained Lock.");
                retVal = SHGetFileInfo(fileName, 0, ref shfi, shfiSize, 0);
            }
            MyLog("Lock released.");
            if (retVal.Equals(IntPtr.Zero))
            {
                MyLog("IntPtr is zero");
                if (tryDisk)
                {
                    if (System.IO.Directory.Exists(fileName))
                        return IconIndex(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), false, iconState);
                    else return IconIndex(fileName, false, iconState);
                }
                else
                    return 0;
            }
            else
            {
                return shfi.iIcon;
            }
        }

        private void MyLog(string val)
        {
            System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.ffff") + " - Thread:" + System.Threading.Thread.CurrentThread.ManagedThreadId + " - Msg:" + val);
        }
    }
}

次のコードを使用して、Webアプリでエラーを再現できます。

protected void Page_Load(object sender, EventArgs e)
{
    Web_ShellIconBug.IconIndexClass ii = new Web_ShellIconBug.IconIndexClass();
    Parallel.ForEach(System.IO.Directory.GetFiles("C:\\Windows"), file =>
    {
        ii.IconIndex(file, false, 0);
    });
    Debug.WriteLine("Done.");
}

Win764ビットとVS2010SP1の両方を実行している2つの異なるマシンでこれを再現しました。私の出力では、次のようなものが表示されます。

21:39:01.7812 - Thread:5 - Msg:Before Lock.
21:39:01.7912 - Thread:5 - Msg:Obtained Lock.
21:39:01.8022 - Thread:5 - Msg:Lock released.
21:39:01.8162 - Thread:10 - Msg:Before Lock.
21:39:02.8382 - Thread:11 - Msg:Before Lock.
21:39:03.8172 - Thread:12 - Msg:Before Lock.
21:39:04.3032 - Thread:5 - Msg:Before Lock.
21:39:04.3032 - Thread:5 - Msg:Obtained Lock.
21:39:04.3042 - Thread:5 - Msg:Lock released.
21:39:04.8162 - Thread:13 - Msg:Before Lock.
...

この場合、スレッド5はロックを取得しているように見えますが、ロックを解放していないため、他のすべてのスレッドは無期限にブロックされます。

注意すべき他のいくつかのこと:

  • デッドロックを再現するのはかなり厄介です。IntPtr.Zeroに等しい戻り値のチェック後に再帰呼び出しのいずれかを変更すると、デッドロックが解消されたように見えますが、それがロックに影響する理由がわからないため、そのコードを変更することを躊躇します問題を修正します。
  • (ロックの代わりに)手動でMonitor.EnterとMonitor.Exitを実行すると、デッドロックは発生しませんが、問題が解決したか、テストケース用に修正したかはわかりません。
  • このコードは、製品版のコードから大幅に削減されているため、クラス内のコードがあまり機能していないように見えるのは、問題を再現しながら、問題からできるだけ多くのノイズを取り除こうとしたためと考えられます。

デッドロックの原因となっている可能性のあるものについて、誰かが洞察を提供できますか?指が上がらないようです。

4

2 に答える 2

1

私がここであなたの最善を提案するかもしれないなら、ぶら下がっているプロセスのダンプを取得し、次にwindbgを使用して分析することです。

開始に役立つように、windbgを使用してデッドロックシナリオを検出する例を次に示します。

ステップ1:シンボルパスを修正する

.symfix c:\ sos .reload

ステップ2:sosをロードする-使用している.netのバージョンをロードするだけです

.load C:\ Windows \ Microsoft.NET \ Framework64 \ v2.0.50727 \ sos

ステップ3:ロードされたモジュールを一覧表示する

。鎖

ステップ4:デッドロックをチェックする-これにより、どのスレッドがハングしたかがわかります

syncblk

ステップ5:そのスレッド番号に切り替えます-この場合は#7です

〜7

ステップ6:その時点でスレッドが何をしていたかをリストします

k

ステップ7:例外がないか確認します

!pe

ステップ8:スレッドに関するより詳細な情報を取得する〜7kL 10

ステップ9:万が一の場合に備えてスタックにエラーがないか確認してください

〜* e!clrstack

于 2012-04-05T12:43:16.747 に答える
0

StructLayoutに問題があったことが判明しました。関数でUnicodeを指定しましたが、構造体で指定しなかったため、デフォルトでANSIになりました。正しい構造体レイアウトは次のようになります。

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
于 2012-04-27T18:45:56.407 に答える