因此,在快速浏览了其中一些和其他类似问题后,我今天下午进行了一场快乐的追逐,试图解决两个独立程序使用文件作为同步(以及文件保存)方法的问题。有点不寻常的情况,但它确实突出了“检查文件是否被锁定,如果不是则打开它”方法的问题。
问题是这样的:在您检查文件和实际打开文件的时间之间,文件可能会被锁定。很难找到零星的无法复制文件,因为如果您不寻找它,它会被另一个进程错误使用。
基本解决方案是尝试在 catch 块内打开文件,这样如果它被锁定,您可以重试。这样,检查和打开之间没有经过的时间,操作系统同时进行。
此处的代码使用 File.Copy,但它同样适用于 File 类的任何静态方法:File.Open、File.ReadAllText、File.WriteAllText 等。
/// <param name="timeout">how long to keep trying in milliseconds</param>
static void safeCopy(string src, string dst, int timeout)
{
while (timeout > 0)
{
try
{
File.Copy(src, dst);
//don't forget to either return from the function or break out fo the while loop
break;
}
catch (IOException)
{
//you could do the sleep in here, but its probably a good idea to exit the error handler as soon as possible
}
Thread.Sleep(100);
//if its a very long wait this will acumulate very small errors.
//For most things it's probably fine, but if you need precision over a long time span, consider
// using some sort of timer or DateTime.Now as a better alternative
timeout -= 100;
}
}
关于并行性的另一个小说明:
这是一种同步方法,它会在等待和处理线程时阻塞其线程。这是最简单的方法,但如果文件长时间保持锁定状态,您的程序可能会变得无响应。并行化是一个太大的话题,无法在这里深入探讨,(您可以设置异步读/写的方式数量有点荒谬),但这是一种可以并行化的方式。
public class FileEx
{
public static async void CopyWaitAsync(string src, string dst, int timeout, Action doWhenDone)
{
while (timeout > 0)
{
try
{
File.Copy(src, dst);
doWhenDone();
break;
}
catch (IOException) { }
await Task.Delay(100);
timeout -= 100;
}
}
public static async Task<string> ReadAllTextWaitAsync(string filePath, int timeout)
{
while (timeout > 0)
{
try {
return File.ReadAllText(filePath);
}
catch (IOException) { }
await Task.Delay(100);
timeout -= 100;
}
return "";
}
public static async void WriteAllTextWaitAsync(string filePath, string contents, int timeout)
{
while (timeout > 0)
{
try
{
File.WriteAllText(filePath, contents);
return;
}
catch (IOException) { }
await Task.Delay(100);
timeout -= 100;
}
}
}
以下是它的使用方法:
public static void Main()
{
test_FileEx();
Console.WriteLine("Me First!");
}
public static async void test_FileEx()
{
await Task.Delay(1);
//you can do this, but it gives a compiler warning because it can potentially return immediately without finishing the copy
//As a side note, if the file is not locked this will not return until the copy operation completes. Async functions run synchronously
//until the first 'await'. See the documentation for async: https://msdn.microsoft.com/en-us/library/hh156513.aspx
CopyWaitAsync("file1.txt", "file1.bat", 1000);
//this is the normal way of using this kind of async function. Execution of the following lines will always occur AFTER the copy finishes
await CopyWaitAsync("file1.txt", "file1.readme", 1000);
Console.WriteLine("file1.txt copied to file1.readme");
//The following line doesn't cause a compiler error, but it doesn't make any sense either.
ReadAllTextWaitAsync("file1.readme", 1000);
//To get the return value of the function, you have to use this function with the await keyword
string text = await ReadAllTextWaitAsync("file1.readme", 1000);
Console.WriteLine("file1.readme says: " + text);
}
//Output:
//Me First!
//file1.txt copied to file1.readme
//file1.readme says: Text to be duplicated!