4

コメントからの解決:
アプリケーションのクラッシュは別の問題が原因で発生しました

2つの異なるアプリケーションからファイルを読み書きしています。ファイルの読み取りまたは書き込み中は、常にアプリAまたはBによってロックされ、両方ともを使用しFileShare.Noneます。

私の問題は、リーダーをtry / catchでラップしても、使用行でIOExceptionが発生してアプリケーションがクラッシュすることです(ライターでは発生しません)。

私はまた、他に違いがないと私が信じているキャッチをcatch (IOException ...作成し、それをより読みやすくしました。

ファイルがロックされているときに無視して、ファイルが使用可能になるまで試行を続ける正しい方法は何ですか?

while (true)
{
    try
    {
        using (FileStream stream = new FileStream("test_file.dat", FileMode.Open, FileAccess.Read, FileShare.None))
        {
            using (TextReader reader = new StreamReader(stream))
            {
                // bla bla bla does not matter
            }
        }
    }
    catch
    {
        // bla bla bla does not matter again
    }
    Thread.Sleep(500);
}

書く

private bool WriteData(string data)
{
    try
    {
        using (FileStream stream = new FileStream("test_file.dat", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
        {
            stream.SetLength(0);
            using (TextWriter writer = new StreamWriter(stream))
            {
                writer.Write(data);
            }
        }
        return true;
    }
    catch
    {
        return false;
    }
}

ファイルが読み取りまたは書き込みのいずれのプロセスでも使用されている場合、私は誰にも共有権(ライターとリーダーの両方の使用)を付与しないことに注意してくださいFileShare.None。基本的に、ファイルが使用可能になるまで例外を処理します。

4

6 に答える 6

5

この目的で使用するコードを次に示します。

/// <summary>
/// Executes the specified action. If the action results in a file sharing violation exception, the action will be
/// repeatedly retried after a short delay (which increases after every failed attempt).
/// </summary>
/// <param name="action">The action to be attempted and possibly retried.</param>
/// <param name="maximum">Maximum amount of time to keep retrying for. When expired, any sharing violation
/// exception will propagate to the caller of this method. Use null to retry indefinitely.</param>
/// <param name="onSharingVio">Action to execute when a sharing violation does occur (is called before the waiting).</param>
public static void WaitSharingVio(Action action, TimeSpan? maximum = null, Action onSharingVio = null)
{
    WaitSharingVio<bool>(() => { action(); return true; }, maximum, onSharingVio);
}

/// <summary>
/// Executes the specified function. If the function results in a file sharing violation exception, the function will be
/// repeatedly retried after a short delay (which increases after every failed attempt).
/// </summary>
/// <param name="func">The function to be attempted and possibly retried.</param>
/// <param name="maximum">Maximum amount of time to keep retrying for. When expired, any sharing violation
/// exception will propagate to the caller of this method. Use null to retry indefinitely.</param>
/// <param name="onSharingVio">Action to execute when a sharing violation does occur (is called before the waiting).</param>
public static T WaitSharingVio<T>(Func<T> func, TimeSpan? maximum = null, Action onSharingVio = null)
{
    var started = DateTime.UtcNow;
    int sleep = 279;
    while (true)
    {
        try
        {
            return func();
        }
        catch (IOException ex)
        {
            int hResult = 0;
            try { hResult = (int) ex.GetType().GetProperty("HResult", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(ex, null); }
            catch { }
            if (hResult != -2147024864) // 0x80070020 ERROR_SHARING_VIOLATION
                throw;
            if (onSharingVio != null)
                onSharingVio();
        }

        if (maximum != null)
        {
            int leftMs = (int) (maximum.Value - (DateTime.UtcNow - started)).TotalMilliseconds;
            if (sleep > leftMs)
            {
                Thread.Sleep(leftMs);
                return func(); // or throw the sharing vio exception
            }
        }

        Thread.Sleep(sleep);
        sleep = Math.Min((sleep * 3) >> 1, 10000);
    }
}

使用例:

Utilities.WaitSharingVio(
    action: () =>
    {
        using (var f = File.Open(file, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
        {
            // ... blah, process the file
        }
    },
    onSharingVio: () =>
    {
        Console.WriteLine("Sharing violation. Trying again soon...");
    }
);
于 2012-10-14T10:50:35.673 に答える
2

Is there a global named reader/writer lock? の情報を使用して、これを一度行いました。.

結果はReaderWriterLockSlim、スレッドではなく複数のプロセスがリソースにアクセスしている場合に機能する のようなものだと思います。

于 2012-10-14T10:21:58.800 に答える
1

ミューテックス オブジェクトを使用して、複数のスレッドまたはプロセスによる同時アクセスから共有リソースを保護できます。

于 2012-10-14T10:19:57.053 に答える
1

以下のように関数を記述して、ファイルのロックを確認できます。

protected bool IsFileLocked(FileInfo file)
{
    FileStream stream = null;

    try
    {
        stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
    }
    catch (IOException)
    {
        //the file is unavailable because it is:
        //still being written to
        //or being processed by another thread
        //or does not exist (has already been processed)
        return true;
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }

    //file is not locked
    return false;
}
于 2012-10-14T10:24:24.527 に答える
0

Timwi からの回答は (別のコンテキストではありますが) 大いに役立ちましたが、すべての IOExceptions から HResult を取得する場合は、フラグ "BindingFlags.Public" も追加する必要があることがわかりました。

public static int GetHresult(this IOException ex)
{
   return (int)ex.GetType().GetProperty("HResult", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(ex, null);
}
于 2013-09-30T09:06:41.027 に答える
-1

リーダーとライターのロックを使用
して、キャッチの正しい構文を使用してください

catch(Exception e)


while (true)
{
    try
    {
        using (FileStream stream = new FileStream("test_file.dat", FileMode.Open, FileAccess.Read, FileShare.None))
        {
            using (TextReader reader = new StreamReader(stream))
            {
                // bla bla bla does not matter
            }
        }
    }
    catch(Exception e)
    {
        // bla bla bla does not matter again
    }
    Thread.Sleep(500);
}
于 2012-10-14T10:13:05.367 に答える