【问题标题】:How do i check if file is in use/busy before continue the rest of the code?在继续其余代码之前,如何检查文件是否正在使用/忙碌?
【发布时间】:2015-11-02 01:02:10
【问题描述】:

我有这部分:

public string SendResponse(HttpListenerRequest request)
        {
            string result = "";
            string key = request.QueryString.GetKey(0);
            if (key == "cmd")
            {
                if (request.QueryString[0] == "uploadstatus")
                {
                    switch (Youtube_Uploader.uploadstatus)
                    {
                        case "uploading file":
                            return "uploading " + Youtube_Uploader.fileuploadpercentages;

                        case "status":
                            return Youtube_Uploader.fileuploadpercentages.ToString();

                        case "file uploaded successfully":
                            Youtube_Uploader.uploadstatus = "";
                            return "upload completed," + Youtube_Uploader.fileuploadpercentages + ","
                               + Youtube_Uploader.time;
                        default:
                            return "upload unknown state";
                    }


                }
                if (request.QueryString[0] == "nothing")
                {
                    return "Connection Success";
                }
                if (request.QueryString[0] == "start")
                {
                    StartRecrod();
                    result = "Recording started";
                }

                if (request.QueryString[0] == "stop")
                {
                    dirchanged = false;
                    StartRecrod();
                    result = "Recording stopped and preparing the file to be shared on youtube";
                    string fileforupload = await WatchDirectory();
                    await WaitForUnlockedFile(fileforupload);
                    using (StreamWriter w = new StreamWriter(userVideosDirectory + "\\UploadedVideoFiles.txt", true))
                    {
                        w.WriteLine(fileforupload);
                    }
                    uploadedFilesList.Add(fileforupload);
                    Youtube_Uploader youtubeupload = new Youtube_Uploader(uploadedFilesList[0]);
                }
            }
            else
            {
                result = "Nothing have been done";
            }

            return result;

        }

然后我有 WatchDirectory 方法:

FileSystemWatcher watcher;

private void WatchDirectory()
{
    watcher = new FileSystemWatcher();
    watcher.Path = userVideosDirectory;
    watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
    watcher.Filter = "*.mp4";
    watcher.Changed += new FileSystemEventHandler(OnChanged);
    watcher.EnableRaisingEvents = true;
}

OnChanged 事件:

private void OnChanged(object source, FileSystemEventArgs e)
{
    var info = new FileInfo(e.FullPath);
    fileforupload = info.FullName;
    while(IsFileLocked(fileforupload) == true)
    {
        System.Threading.Thread.Sleep(100);
    }
}

然后是 IsFileLocked 方法:

public bool IsFileLocked(string filename)
{
    bool Locked = false;
    try
    {
        FileStream fs =
                    File.Open(filename, FileMode.OpenOrCreate,
                    FileAccess.ReadWrite, FileShare.None);
        fs.Close();
    }
    catch (IOException ex)
    {
        Locked = true;
    }
    return Locked;
}

现在的顺序应该是首先它会转到 WatchDirectory,然后在 while 循环中检查文件是否被锁定/忙碌,一旦文件不再锁定/忙碌,继续使用 StreamWriter 的其余代码UploadedFilesList.Add 和 Youtube_Uploader...

首先我不确定这是否是使用 100 的 While 循环的正确方法。 其次,我如何使它在继续之前首先完成文件锁定检查?现在它所做的是到达 WatchDirectory 然后制作 StreamWriter...不是我想要的顺序。

【问题讨论】:

  • 答案比我想在星期六早上写出来的还要复杂,又累又累。我会说你需要知道这些事情:WatchDirectory 立即返回,它不等待事件。 OnChanged 事件在单独的线程上引发。您应该使用BackgroundWorkerManualResetEvents 在文件发生更改或解锁时发出信号。不要轮询锁定状态;要么使用计时器,要么看看FileSystemWatcher 是否可以做到。

标签: c# .net winforms


【解决方案1】:

你的代码最大的问题是它没有在任何有用的地方等待,而是在你想要的最后一个地方等待:

  • 您致电WatchDirectory(),然后立即着手创建您的作家。 WatchDirectory() 方法中没有任何东西会延迟它的返回,所以当然你在发生任何事情之前继续下一条语句。
  • OnChanged() 方法中,您轮询文件锁定状态。但此方法是FileSystemWatcher 事件的事件处理程序,将在您确实不/不应该延迟线程的上下文中调用。

我会更改您的代码以利用async 模式,不仅可以解决上述问题,还可以提供异步操作,即防止此逻辑在等待时阻止程序的其余部分在被监视的目录中发生了一些有趣的事情。

以下是我认为更好的方法的新版本:

private async Task<string> WatchDirectoryAsync()
{
    using (FileSystemWatcher watcher = new FileSystemWatcher())
    {
        TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();

        watcher.Path = userVideosDirectory;
        watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
        watcher.Filter = "*.mp4";
        watcher.Changed += (sender, e) => tcs.SetResult(e.FullPath);
        watcher.EnableRaisingEvents = true;

        return await tcs.Task;
    }
}

// You can get rid of the OnChanged() method altogether

private async Task WaitForUnlockedFileAsync(string fileName)
{
    while (true)
    {
        try
        {
            using (IDisposable stream = File.Open(fileName, FileMode.OpenOrCreate,
                FileAccess.ReadWrite, FileShare.None))
            { /* on success, immediately dispose object */ }

            break;
        }
        catch (IOException)
        {
            // ignore exception
            // NOTE: for best results, consider checking the hresult value of
            // the exception, to ensure that you are only ignoring the access violation
            // exception you're expecting, rather than other exceptions, like
            // FileNotFoundException, etc. which could result in a hung process
        }

        // You might want to consider a longer delay...maybe on the order of
        // a second or two at least.
        await Task.Delay(100);
    }
}

然后你可以像这样使用它:

if (request.QueryString[0] == "stop")
{
    dirchanged = false;
    StartRecrod();
    result = "Recording stopped and preparing the file to be shared on youtube";
    string fileforupload = await WatchDirectoryAsync();
    await WaitForUnlockedFileAsync(fileforupload);
    using (StreamWriter w = new StreamWriter(userVideosDirectory + "\\UploadedVideoFiles.txt",true))
    {
        w.WriteLine(fileforupload);
    }
    uploadedFilesList.Add(fileforupload);                   
    Youtube_Uploader youtubeupload = new Youtube_Uploader(uploadedFilesList[0]);
}

当然,要在上面使用await,代码需要包含在async 方法中。如果没有 a good, minimal, complete code example 显示整个上下文,就不可能说出您将如何将其作为一个整体整合到程序中。但是有很多关于 Stack Overflow 和其他地方关于该主题的建议。基本思想是,通常,调用方法都必须转换为async 方法,直到调用链开始的堆栈顶部(通常是某种事件处理程序,在用户执行时调用某种输入)。

在某些情况下,您可以只调用async 方法并忽略返回的Task 对象引用(不理想),或者推迟处理返回值(更好)。您必须根据自己的情况决定最适合您的情况。


编辑:

如果您不能或不会将原来的调用方法更改为async 方法,则可以同步执行这些操作。它们可以自己实现为同步方法,如下所示:

private string WatchDirectory()
{
    using (FileSystemWatcher watcher = new FileSystemWatcher())
    {
        TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();

        watcher.Path = userVideosDirectory;
        watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
        watcher.Filter = "*.mp4";
        watcher.Changed += (sender, e) => tcs.SetResult(e.FullPath);
        watcher.EnableRaisingEvents = true;

        return tcs.Task.Result;
    }
}

// You can get rid of the OnChanged() method altogether

private void WaitForUnlockedFile(string fileName)
{
    while (true)
    {
        try
        {
            using (IDisposable stream = File.Open(fileName, FileMode.OpenOrCreate,
                FileAccess.ReadWrite, FileShare.None))
            { /* on success, immediately dispose object */ }

            break;
        }
        catch (IOException)
        {
            // ignore exception
            // NOTE: for best results, consider checking the hresult value of
            // the exception, to ensure that you are only ignoring the access violation
            // exception you're expecting, rather than other exceptions, like
            // FileNotFoundException, etc. which could result in a hung process
        }

        // You might want to consider a longer delay...maybe on the order of
        // a second or two at least.
        Thread.Sleep(100);
    }
}

或者您可以简单地同步使用异步实现的操作:

if (request.QueryString[0] == "stop")
{
    dirchanged = false;
    StartRecrod();
    result = "Recording stopped and preparing the file to be shared on youtube";
    string fileforupload = WatchDirectoryAsync().Result;
    WaitForUnlockedFileAsync(fileforupload).Wait();
    using (StreamWriter w = new StreamWriter(userVideosDirectory + "\\UploadedVideoFiles.txt",true))
    {
        w.WriteLine(fileforupload);
    }
    uploadedFilesList.Add(fileforupload);                   
    Youtube_Uploader youtubeupload = new Youtube_Uploader(uploadedFilesList[0]);
}

例如,如果您打算最终将其他代码转换为 async 但由于某种原因现在不能,您可以选择后者。

请注意,我不建议这种方法。这些操作本质上是异步的;即它们依赖并等待一些本身没有同步发生的外部活动。因此,从长远来看,如果您的程序在等待外部操作时不停止其进程,您的程序会运行得更好

【讨论】:

  • Peter 对不起,我没有在我的请求问题中添加完整的方法。QueryString 我现在将它添加到问题中,如果您现在可以查看它。 if (request.QueryString[0] == "stop") 所在的整个方法是字符串类型,它是我的 Web 服务器的一部分,在这一部分中,我正在获取命令,然后做一些事情,例如: if (request. QueryString[0] == "停止") ...
  • 彼得问题是该方法是字符串类型,所以我在这一行的等待行上遇到两个错误:string fileforupload = await WatchDirectory();错误 4 'await' 运算符只能在异步方法中使用。考虑使用 'async' 修饰符标记此方法并将其返回类型更改为 'Task'。
  • 彼得和这一行:等待 WaitForUnlockedFile(fileforupload);错误 5 'await' 运算符只能在异步方法中使用。考虑使用 'async' 修饰符标记此方法并将其返回类型更改为 'Task'。
  • @Danielvanwolf:您是否尝试过遵循编译器错误消息的建议? IE。将方法声明更改为public async Task&lt;string&gt; SendResponseAsync(HttpListenerRequest request)(注意,按照惯例,在async 方法的实际名称中使用“Async”一词)。
  • @Danielvanwolf:正如我在回答中提到的,使用await 要求该方法是async 方法。当然,将这个方法转换为async 方法会依次改变它的调用方式。没有a good, minimal, complete code example,就不可能提供具体的建议,因此我的回答中提供了笼统的建议。
猜你喜欢
  • 2015-02-21
  • 2019-01-19
  • 1970-01-01
  • 2011-12-22
  • 1970-01-01
  • 1970-01-01
  • 2012-03-15
  • 2019-12-24
  • 2012-07-02
相关资源
最近更新 更多