等到文件在.NET中解鎖


6 Answers

從Eric的回答開始,我包含了一些改進,使代碼更加緊湊和可重用。 希望它是有用的。

FileStream WaitForFile (string fullPath, FileMode mode, FileAccess access, FileShare share)
{
    for (int numTries = 0; numTries < 10; numTries++) {
        FileStream fs = null;
        try {
            fs = new FileStream (fullPath, mode, access, share);
            return fs;
        }
        catch (IOException) {
            if (fs != null) {
                fs.Dispose ();
            }
            Thread.Sleep (50);
        }
    }

    return null;
}
Question

阻塞線程的最簡單方法是什麼,直到文件解鎖並且可以讀取和重命名為止? 例如,.NET Framework中是否有WaitOnFile()?

我有一個使用FileSystemWatcher查找要傳輸到FTP站點的文件的服務 ,但是文件創建的事件在另一個進程完成寫入文件之前觸發。

理想的解決方案會有一個超時期限,因此線程在放棄之前不會永遠掛起。

編輯:在嘗試了下面的一些解決方案後,我最終更改了系統,以便所有文件寫入Path.GetTempFileName() ,然後執行File.Move()到最終位置。 一旦FileSystemWatcher事件觸發,文件就已經完成。




添加Outlook附件時遇到類似問題。 “使用”節省了一天的時間。

string fileName = MessagingBLL.BuildPropertyAttachmentFileName(currProp);

                //create a temporary file to send as the attachment
                string pathString = Path.Combine(Path.GetTempPath(), fileName);

                //dirty trick to make sure locks are released on the file.
                using (System.IO.File.Create(pathString)) { }

                mailItem.Subject = MessagingBLL.PropertyAttachmentSubject;
                mailItem.Attachments.Add(pathString, Outlook.OlAttachmentType.olByValue, Type.Missing, Type.Missing);



在大多數情況下,像@harpo建議的簡單方法會起作用。 您可以使用這種方法開發更複雜的代碼:

  • 使用SystemHandleInformation \ SystemProcessInformation查找選定文件的所有打開的句柄
  • 子類WaitHandle類可以訪問它的內部句柄
  • 將發現的句柄傳遞給WaitHandle.WaitAny方法的子類WaitHandle中



我之前用過的技術之一是寫我自己的功能。 基本上捕捉異常,並使用一個可以在指定持續時間內觸發的計時器重試。 如果有更好的方法,請分享。




我為這類事情扔了一個助手班。 如果你能夠控制所有可以訪問文件的東西,它就會工作。 如果你期待其他一些事情的爭論,那麼這是非常沒有價值的。

using System;
using System.IO;
using System.Threading;

/// <summary>
/// This is a wrapper aroung a FileStream.  While it is not a Stream itself, it can be cast to
/// one (keep in mind that this might throw an exception).
/// </summary>
public class SafeFileStream: IDisposable
{
    #region Private Members
    private Mutex m_mutex;
    private Stream m_stream;
    private string m_path;
    private FileMode m_fileMode;
    private FileAccess m_fileAccess;
    private FileShare m_fileShare;
    #endregion//Private Members

    #region Constructors
    public SafeFileStream(string path, FileMode mode, FileAccess access, FileShare share)
    {
        m_mutex = new Mutex(false, String.Format("Global\\{0}", path.Replace('\\', '/')));
        m_path = path;
        m_fileMode = mode;
        m_fileAccess = access;
        m_fileShare = share;
    }
    #endregion//Constructors

    #region Properties
    public Stream UnderlyingStream
    {
        get
        {
            if (!IsOpen)
                throw new InvalidOperationException("The underlying stream does not exist - try opening this stream.");
            return m_stream;
        }
    }

    public bool IsOpen
    {
        get { return m_stream != null; }
    }
    #endregion//Properties

    #region Functions
    /// <summary>
    /// Opens the stream when it is not locked.  If the file is locked, then
    /// </summary>
    public void Open()
    {
        if (m_stream != null)
            throw new InvalidOperationException(SafeFileResources.FileOpenExceptionMessage);
        m_mutex.WaitOne();
        m_stream = File.Open(m_path, m_fileMode, m_fileAccess, m_fileShare);
    }

    public bool TryOpen(TimeSpan span)
    {
        if (m_stream != null)
            throw new InvalidOperationException(SafeFileResources.FileOpenExceptionMessage);
        if (m_mutex.WaitOne(span))
        {
            m_stream = File.Open(m_path, m_fileMode, m_fileAccess, m_fileShare);
            return true;
        }
        else
            return false;
    }

    public void Close()
    {
        if (m_stream != null)
        {
            m_stream.Close();
            m_stream = null;
            m_mutex.ReleaseMutex();
        }
    }

    public void Dispose()
    {
        Close();
        GC.SuppressFinalize(this);
    }

    public static explicit operator Stream(SafeFileStream sfs)
    {
        return sfs.UnderlyingStream;
    }
    #endregion//Functions
}

它使用一個有名的互斥體。 那些希望訪問該文件的人試圖獲得對指定互斥體的控制權,該互斥體共享文件的名稱('\'變成'/')。 您可以使用Open(),它會停止,直到可以訪問互斥鎖或者您可以使用TryOpen(TimeSpan),TryOpen(TimeSpan)會嘗試獲取給定持續時間的互斥鎖,如果在時間跨度內無法獲取,則返回false。 這應該最有可能在一個使用塊內部使用,以確保正確釋放鎖,並且當該對像被丟棄時流(如果打開)將被正確處置。

我做了一個快速測試,用〜20個事情來做文件的各種讀/寫操作,並且沒有發現損壞。 顯然它不是很先進,但它應該適用於大多數簡單情況。




我不知道你用什麼來確定文件的鎖定狀態,但是這樣的事情應該這樣做。

while (true)
{
    try {
        stream = File.Open( fileName, fileMode );
        break;
    }
    catch( FileIOException ) {

        // check whether it's a lock problem

        Thread.Sleep( 100 );
    }
}



我和Gulzar一樣,只是不停地嘗試一下循環。

事實上,我甚至不打擾文件系統觀察者。 每分鐘一次輪詢網絡驅動器獲取新文件便宜。




Related