C# 从多个应用程序读取/写入文件

标签 c# multithreading winforms

我有一个场景,我们正在维护 Rates 文件 (.xml),该文件由运行在 3 个不同服务器上的 3 个不同应用程序访问。所有 3 个应用程序都使用 RatesMaintenance.dll,它具有以下 4 种方法加载、写入、读取和关闭。

所有 3 个应用程序都连续写入文件,因此我添加了 Monitor.Enter 和 Monitor.Exit 机制,假设来自 3 个不同应用程序的这 3 个操作不会发生冲突。但此刻,在某些情况下,我收到错误消息 - “无法打开费率文件

据我了解,这意味着,出于某种原因,3 个应用程序试图同时访问。 有人可以建议如何处理这种情况吗?

        Monitor.Enter(RatesFileLock);

        try
        {
            //Open Rates file
            LoadRatesFile(false);

            //Write Rates into file
            WriteRatesToFile();

            //Close Rates file
            CloseRatesFile();
        }
        finally
        {
            Monitor.Exit(RatesFileLock);
        }

Load-的方法签名

LoadRatesFile(bool isReadOnly)

用于打开文件-

new FileStream(RatesFilePath,
        isReadOnly ? FileMode.Open : FileMode.OpenOrCreate,
        isReadOnly ? FileAccess.Read : FileAccess.ReadWrite,
                isReadOnly ? FileShare.ReadWrite : FileShare.None);

.... remaining Rates reading logic code here

用于从文件中读取汇率-

Rates = LoadRatesFile(true);

将利率写入文件-

if (_RatesFileStream != null && _RatesInfo != null && _RatesFileSerializer != null)
            {
                    _RatesFileStream.SetLength(0);

                    _RatesFileSerializer.Serialize(_RatesFileStream, _RatesInfo);
            }

在关闭文件的方法中-

            _RatesFileStream.Close();
            _RatesFileStream = null;

我希望,我会尝试详细解释我的场景。如果有人了解更多详情,请告诉我。

最佳答案

虽然其他答案是正确的,并且您无法通过多个进程并发访问的文件获得完美解决方案,但添加重试机制可能会使其可靠足以您的用例。

在展示一种方法之前,我有两个小建议 - C# 的“using” block 对于处理文件和锁等资源非常有用,您确实希望在使用后处理这些资源。在您的代码中,监视器始终退出,因为您使用了 try..finally(尽管使用外部“锁定” block 仍然会更清楚)但是如果 WriteRatesToFile 方法失败,您不会关闭文件。

因此,首先,我建议将您的代码更改为类似以下内容 -

private static object _ratesFileLock = new object();

public void UpdateRates()
{
    lock (_ratesFileLock)
    {
        using (var stream = GetRatesFileStream())
        {
            var rates = LoadRatesFile(stream);

            // Apply any other update logic here

            WriteRatesToFile(rates, stream);
        }
    }
}

private Stream GetRatesFileStream()
{
    return File.Open("rates.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
}

private IEnumerable<Rate> LoadRatesFile(Stream stream)
{
    // Apply any other logic here
    return RatesSerialiser.Deserialise(stream);
}

private void WriteRatesToFile(IEnumerable<Rate> rates, Stream stream)
{
    RatesSerialiser.Serialise(rates, stream);
}

这尝试打开文件流一次,然后在加载和写入操作之间重用它 - 并可靠地处理它,即使在使用 block 中遇到错误(同样适用于“锁定” block ,这是比 Monitor.Enter/Exit 和 try..finally 更简单)。

这可以很简单地扩展为包括重试机制,这样如果文件被另一个进程锁定,那么我们等待一小段时间然后重试 -

private static object _ratesFileLock = new object();

public void UpdateRates()
{
    Attempt(TryToUpdateRates, maximumNumberOfAttempts: 50, timeToWaitBetweenRetriesInMs: 100);
}

private void TryToUpdateRates()
{
    lock (_ratesFileLock)
    {
        using (var stream = GetRatesFileStream())
        {
            var rates = LoadRatesFile(stream);

            // Apply any other update logic here

            WriteRatesToFile(rates, stream);
        }
    }
}

private Stream GetRatesFileStream()
{
    return File.Open("rates.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
}

private IEnumerable<Rate> LoadRatesFile(Stream stream)
{
    // Apply any other logic here
    return RatesSerialiser.Deserialise(stream);
}

private void WriteRatesToFile(IEnumerable<Rate> rates, Stream stream)
{
    RatesSerialiser.Serialise(rates, stream);
}

private static void Attempt(Action work, int maximumNumberOfAttempts, int timeToWaitBetweenRetriesInMs)
{
    var numberOfFailedAttempts = 0;
    while (true)
    {
        try
        {
            work();
            return;
        }
        catch
        {
            numberOfFailedAttempts++;
            if (numberOfFailedAttempts >= maximumNumberOfAttempts)
                throw;
            Thread.Sleep(timeToWaitBetweenRetriesInMs);
        }
    }
}

关于C# 从多个应用程序读取/写入文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38150840/

相关文章:

c# - Lucene.NET 并搜索具有特定值的多个字段

c# - 将 List 初始化为静态方法

c# - WPF ComboBox...如何设置 .Text 属性?

java - 不同 JVM 中的 Java 程序如何在无意中相互影响?

java - 让java程序的一部分等待......而不是整个程序

python - 为什么要锁定线程?

c# - 通过 C# 将 JSON 数据 POST 到 PHP 并将值存储在 MYSQL 中

c# - 当 openFileDialog 从第二个表单返回结果时,Modalform 关闭

c# - 如何在没有 Control.Invoke() 的情况下从后台线程修改控件属性

c# - 从 WinForms 应用程序中的另一个线程访问由 GUI 工作线程创建的按钮