【问题标题】:Correct way to read\write to a file: heavily used application读取\写入文件的正确方法:大量使用的应用程序
【发布时间】:2012-04-13 19:22:23
【问题描述】:

我们有一个使用率很高的 .Net 3.5 应用程序,它读取“创建成本高”的数据并将其缓存。应用程序基于它而不是“被另一个进程使用”来读取\写入文件。如果其他进程正在读取和写入文件,则应用程序进入睡眠状态(一段时间)并重试。这是读写文件的正确方法吗?请指教。

public void Add<T>(string key, CacheItem<T> item)
        {
            bool fileInUse = false;
            while (!fileInUse)
            {
                try
                {
                    using (Stream stream = new FileStream(Path.Combine(cachePath, key+".bin"), FileMode.Create, FileAccess.Write, FileShare.None))
                    {
                        Serializer.NonGeneric.Serialize(stream, item);
                    }
                    fileInUse = true;
                }
                catch (IOException ex)
                {
                    if (ex.Message.Contains("being used by another process"))
                    {
                        //Poll till the file is free to be used by this process
                        Thread.Sleep(100);
                        fileInUse = false;
                    }
                }
            }            
        }        

public CacheItem<T> Get<T>(string key, Type type)
        {
            CacheItem<T> item = null;

            FileInfo fileInfo = new FileInfo(Path.Combine(cachePath, key+".bin"));
            fileInfo.Refresh();
            if (fileInfo.Exists)
            {
                bool fileInUse = false;
                while (!fileInUse)
                {
                    try
                    {
                        using (Stream stream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.None))
                        {
                            object objectTemp = Serializer.NonGeneric.Deserialize(type, stream);
                            item = (CacheItem<T>)objectTemp;
                        }
                        fileInUse = true;
                    }
                    catch(IOException ex) 
                    {
                        if (ex.Message.Contains("being used by another process"))
                        {
                            //Poll till the file is free to be used by this process
                            Thread.Sleep(100);
                            fileInUse = false;
                        }
                    }
                }               
            }                       
            return item;                                   
        }

【问题讨论】:

    标签: c# asp.net .net protobuf-net


    【解决方案1】:

    您可以在其上添加一个全局互斥锁,以避免等待超过绝对必要的时间。

    通过将非空name 传递给Mutex Constructor 来创建全局互斥锁。

    好处:

    • Mutex 允许您在文件可用后立即唤醒,而不是平均等待 50 毫秒。
    • Mutex 允许您入睡一次并醒来一次,而不是反复睡觉/醒来。操作系统可以非常有效地处理休眠的线程,并且几乎不消耗任何资源。
    • 获得互斥锁后,打开文件几乎 100% 成功,而不是在成功之前可能失败多次。

    总而言之,您不仅会更快,而且在此过程中可能会消耗更少的 CPU 周期

    【讨论】:

    • @PankajGarg 我确信我可以,但那会完成什么呢?这个机制是如何运作的还有什么不清楚的地方吗?
    • 布兰科;这是我们的一位架构师所说的=>“我对使用 Mutex 类的保留意见是,它在计算上比使用 lock 语句或 Monitor 类更昂贵,这违背了我们提高性能的目的。”有什么想法吗?
    • @AjitGoel 是,但它可以同步 separate 进程,这是 Monitor 无法做到的。另外,请参阅我的编辑。
    【解决方案2】:

    如果您经常这样做(因此性能是一个问题),我会建议完全不同的设计。

    你应该有一个公共静态方法(或单例中的方法),它接受一个字符串(如果它适用于多个文件,还有一个文件名)。在该方法中,它应该将该字符串放入BlockingCollection&lt;string&gt;。 (您可以通过 Dictionary&lt;string, BlockingCollection&lt;string&gt;&gt; 将文件名映射到该文件的队列,每个文件或每个文件只有一个。)

    在大多数情况下(即缓冲区未满),想要将内容写入文件的任务只需将其添加到队列中,然后立即恢复工作。

    然后你需要有一个单独的线程/任务,它只是坐在那里从阻塞集合中读取(你应该只需要一个,即使你有很多阻塞集合)并将数据写入文件。由于只有一个线程写入文件,因此不需要锁定文件 IO,BlockingCollection 旨在在此生产者/消费者模型中工作,并为您处理所有需要的锁定。

    我建议对所有文件只使用一个BlockingColleciton,除非您找到一个令人信服的性能理由来尝试处理多个队列。管理起来会容易一些。

    【讨论】:

    • 感谢服务。我忘记补充说我们的应用程序是一个 .Net 3.5 应用程序。 BlockingCollection 是 .Net 4.0 的一部分。
    • @AjitGoel 你可以用ConcurrentQueue 代替。这有点不方便,但你可以。
    • ConcurrentQueue 是 .Net 4.0 及以上?我不能在我们的应用程序中使用。
    • @AjitGoel 嗯...我可以发誓 ConcurrentQueue 是 3.5。可能不会。哦,好吧,只需使用 Queue 并将 lock 声明任何可以访问它的东西,就足够了。
    猜你喜欢
    • 1970-01-01
    • 2014-01-15
    • 2016-06-05
    • 1970-01-01
    • 2013-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-26
    相关资源
    最近更新 更多