c# - 文件被锁定时避免崩溃?

标签 c# .net-4.0 concurrency filelock

来自评论的决议:
应用程序崩溃是由另一个问题引起的

我正在从 2 个不同的应用程序读取/写入一个文件,当文件被读取或写入时,它总是被应用程序 A 或 B 锁定,它们都使用 FileShare.None.

我的问题是,即使将 reader 包装在 try/catch 周围,它仍然会使应用程序在 using 行崩溃并出现 IOException(不会发生在 writer 身上)。

我也将 catch 设置为 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)授予任何人,所以基本上我正在处理异常,直到文件可用,这是不工作的。

最佳答案

这是我们为此目的使用的代码。

/// <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...");
    }
);

关于c# - 文件被锁定时避免崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12881254/

相关文章:

c# - 以编程方式触发选择事件

c# - 在 C# 中将类序列化为 'almost' xml 字符串

multithreading - 在单例 session Bean 中管理并发访问

c# - NHibernate 是否具有创建和更新表的功能,如 EF Code First 的自动迁移功能?

c# - HttpWebRequest 在 VS for Win Phone 上抛出 "The remote server returned an error: NotFound."

c# - c# 原始数组是 volatile 的吗?

c# - 多线程网络抓取工具?

azure - WindowsAzure.Storage SDK 缺少 .NET Standard 中的 API

Java 内部和显式全局锁定的等效性

java - 通知所有()不起作用