.NET 6學習筆記(1)——通過FileStream實現不同程式對單一檔案的同時讀寫

樓上那個蜀黍發表於2022-02-25

會寫這篇純屬機緣巧合,雖然一直以來認為對單一檔案的讀、寫操作是不衝突,可並行的,但實際並未實踐過。正好有個UWP的程式要並行讀取由Desktop Extension建立的文字,需要有個原型程式來驗證,那不妨點開最新的VS 2022,順手試試新的語法糖。
首先我們明確本篇對檔案的操作均通過FileStream類來實現,FileStream在.NET 6進行了完全的重寫,提高了效能和可靠性。但是本篇提到的共享讀寫許可權,在之前版本也是完全支援的。
本篇提到的同時讀寫功能依賴FileStream的這個建構函式:

public FileStream (string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share);

接下來我們通過實際的程式碼來進行分析。建立第一個工程CreateWriteSharedFile,該工程為.NET 6的Console程式,用於新建和寫入內容到名為TestFile.txt的檔案中。

var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "TestFile.txt");
var fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
StreamWriter sw = new StreamWriter(fileStream);
int cout = 0;
while (true)
{
    for (var i = 0; i < 10; i++)
    {
        sw.WriteLine(cout++);
        Console.WriteLine(cout);
    }
    sw.Flush();
    await Task.Delay(1000);
}

沒有名稱空間,沒有類名和Main函式,這是C# 10裡的新語法糖——頂級語句。作為簡化後的程式入口點,十分適合簡短的示例程式,對初學者也更友好。

程式碼的內容也很好懂,就是每隔1秒連續寫入10個自增的數字。唯一值得留意的是FileShare.ReadWrite,這個列舉標識對應的是後續其他對該檔案的請求,不管是該程式內還是另外程式,均給與ReadWrite的許可權。

我們的第二個工程ReadSharedFile僅做讀取的操作,所以上面CreateWriteSharedFile中的FileShare只給Read也可以。但是相反,ReadSharedFile因為要允許CreateWriteSharedFile來進行寫操作,所以它必須給與FileShare.Write列舉。

var path =Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "TestFile.txt");
var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read,FileShare.Write);
var reader = new StreamReader(fileStream);

while (!reader.EndOfStream)
{
    Console.WriteLine(reader.ReadLine());
    await Task.Delay(1000);
}

上述程式碼是在ReadSharedFile工程中讀取由CreateWirteSharedFile建立的TestFile.txt中的內容。想要測試的話,build成功後執行對應的exe檔案即可。並行的讀和寫操作較為容易理解,也不會存在衝突或生成髒資料的問題。

但如果是同時進行寫操作會怎麼樣呢?之前的FileShare.ReadWrite就是為接下來的測試準備的。我們建立第二個寫檔案的工程SecondWriteSharedFile,同樣要注意除了設定Read以外,還要為CreateWriteSharedFile特別準備Write許可權,才能實現兩邊同時寫入該檔案的要求。

var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "TestFile.txt");
var fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
StreamWriter sw = new StreamWriter(fileStream);

while (true)
{
    for (var i = 0; i < 10; i++)
    {
        sw.WriteLine("A".PadRight(i,'A'));
        Console.WriteLine("A".PadRight(i, 'A'));
    }

    sw.Flush();
    await Task.Delay(1000);
}

非常不幸的是,SecondWriteSharedFile在預設情況下,同樣會從檔案的頭部開始寫入,這樣就覆蓋了先執行的CreateWriteSharedFile在同樣位置寫入的內容。所以在一般情況下,我們要避免並行的寫操作,這樣極容易互相覆蓋產生髒資料。
本篇簡單地討論了通過FileShare列舉,使用FileStream並行地讀寫檔案的一般場景。希望能夠拋磚引玉,給各位大佬在實際生產場景中以微小的幫助。
示例程式碼:(因為GitHub經常打不開,我在gitee也同樣放了一份)

https://github.com/manupstairs/FileReadWriteSample
https://gitee.com/manupstairs/FileReadWriteSample

以下連結,是MS Learn上Windows開發的入門課程,單個課程三十分鐘到60分鐘不等,想要補充基礎知識的同學點這裡:

開始使用 Visual Studio 開發 Windows 10 應用

開發 Windows 10 應用程式

編寫首個 Windows 10 應用

建立 Windows 10 應用的使用者介面 (UI)

增強 Windows 10 應用的使用者介面

在 Windows 10 應用中實現資料繫結

相關文章