【问题标题】:Why sometimes when watching a file/directory for changes it's not changing the flag to true?为什么有时在查看文件/目录的更改时它没有将标志更改为真?
【发布时间】:2015-08-11 04:43:50
【问题描述】:

在构造函数中,我正在调用 WatchDirectory 方法:

private void WatchDirectory()
        {
            FileSystemWatcher 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)
    {
        try
        {
            var info = new FileInfo(e.FullPath);
            fileforupload = info.FullName;
            if (e.ChangeType == WatcherChangeTypes.Changed)
            {
                var theSize = info.Length;

                label2.BeginInvoke((Action)(() =>
                {
                    label2.Text = theSize.ToString();
                }));
            }
            dirchanged = true;
        } 
        catch (Exception ee)
        {
            string err = ee.ToString();
        }
    }

然后我使用 while 循环来检查 dirchange 标志何时为真:

                    WatchDirectory();
                    while (dirchanged == false)
                    {
                        if (dirchanged == true)
                        {
                            Youtube_Uploader youtubeupload = new 
                                            Youtube_Uploader(fileforupload);
                            break;
                        }
                    }

问题在于,有时它永远不会在 OnChanged 事件中将 dirchanged 更改为 true。不知道为什么。它似乎触发了 OnChanged 事件,但有时它不执行 dirchanged = true;

因此在while 循环内dirchanged 标志始终保持false

我现在添加了一个新方法,我称之为 IsFileLocked:

protected virtual bool IsFileLocked(FileInfo file)
        {
            FileStream stream = null;

            try
            {
                stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
            }
            catch (IOException)
            {
                return true;
            }
            finally
            {
                if (stream != null)
                    stream.Close();
            }
            return false;
        }

我在 OnChanged 事件中使用它:

private void OnChanged(object source, FileSystemEventArgs e)
        {
            try
            {
                var info = new FileInfo(e.FullPath);
                fileforupload = info.FullName;
                IsFileLocked(info);
                if (e.ChangeType == WatcherChangeTypes.Changed)
                {
                    var theSize = info.Length;

                    label2.BeginInvoke((Action)(() =>
                    {
                        label2.Text = theSize.ToString();
                    }));
                }
                dirchanged = true;
            }
            catch (Exception ee)
            {
                string err = ee.ToString();
            }
        }

在 IsFileLocked 方法中,我遇到了异常:

该进程无法访问文件“C:\Users\bout0_000\Videos\My Great Game - My Great Capture - 2015-08-10 14-22-52.mp4”,因为它正被另一个进程使用。

我正在使用创建文件的外部程序,由于该程序仍在创建文件,因此观察者无法访问它。 所以我在这里有一个冲突,一方面我想知道文件何时完成创建,但另一方面我不知道,因为外部程序仍在处理它。

那么我怎样才能知道外部程序何时完成对文件的处理并且文件准备好了?

这是while代码的全部部分:

if (request.QueryString[0] == "stop")
                    {
                        dirchanged = false; 
                        StartRecrod();                        
                        result = "Recording stopped and preparing the file to be shared on youtube";
                        WatchDirectory();
                        while (dirchanged == false)

                        {
                            if (dirchanged == true)
                            {
                                string ttttt = "ok";
                                break;
                            }
                        }
                    }

我添加了一个字符串 ttttt 只是为了测试。 有时在使用断点时会到达字符串 ttttt,有时不会。

在我的程序中,当我触摸我的 android 屏幕时,它会向 pc 网络服务器发送命令,它会到达这里,但是 while 循环有问题,并且有时会更改标志,它确实会进入 while 和 IF 并执行字符串 ttttt有时不会。

这就是我现在对 await 所做的:

TaskCompletionSource<bool> sy;
        public async void SendResponse(HttpListenerRequest request)
        {
            string result = "";
            string key = request.QueryString.GetKey(0);
            if (key == "cmd")
            {
                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";
                    sy = new TaskCompletionSource<bool>();
                    WatchDirectory();
                    await sy.Task;
                    Youtube_Uploader youtubeupload = new Youtube_Uploader(fileforupload);


                }
            }
            else
            {
                result = "Nothing have been done";
            }
            if (Youtube_Uploader.fileuploadedsuccess != null && Youtube_Uploader.fileuploadedsuccess != "")
            {
                result = Youtube_Uploader.fileuploadedsuccess;
            }

            return result;
        }

但是有些问题。 首先,我在所有退货中都遇到了错误。

错误 2 由于 'Automatic_Record.Form1.SendResponse(System.Net.HttpListenerRequest)' 返回 void,return 关键字后面不能跟对象表达式

初始化我的网络服务器时出错:

WebServer ws = new WebServer(SendResponse, "http://+:8098/");

在我得到的 SendResponse 上:

错误 1 ​​'void Automatic_Record.Form1.SendResponse(System.Net.HttpListenerRequest)' 的返回类型错误

现在将方法更改为异步时会发生此错误。

这是我的 WebServer 方法,我在初始化时出错,因为它应该得到其他东西然后异步:

public WebServer(Func<HttpListenerRequest, string> method, params string[] prefixes)
            : this(prefixes, method) { }

        public void Run()
        {
            ThreadPool.QueueUserWorkItem((o) =>
            {
                Console.WriteLine("Webserver running...");
                try
                {
                    while (_listener.IsListening)
                    {
                        ThreadPool.QueueUserWorkItem((c) =>
                        {
                            var ctx = c as HttpListenerContext;
                            try
                            {
                                string rstr = _responderMethod(ctx.Request);
                                System.Diagnostics.Trace.Write(ctx.Request.QueryString);
                                //ctx.Request.QueryString

                                byte[] buf = Encoding.UTF8.GetBytes(rstr);
                                ctx.Response.ContentLength64 = buf.Length;
                                ctx.Response.OutputStream.Write(buf, 0, buf.Length);
                                System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder();

                            }
                            catch { } // suppress any exceptions
                            finally
                            {
                                // always close the stream
                                ctx.Response.OutputStream.Close();
                            }
                        }, _listener.GetContext());
                    }
                }
                catch { } // suppress any exceptions
            });
        }

【问题讨论】:

  • 好吧,您是否尝试在异常捕获中设置断点?因为像现在这样,你永远不会知道是否抛出了异常,因为你只是创建了一个新字符串,但什么也没做。
  • 我添加了一个新方法 IsFileLocked 并在 OnChanged 事件中使用它,我也用它更新了我的问题。我发现我的观察者与在硬盘上创建文件的外部程序发生冲突。我对观察者的主要想法是检查文件大小的变化,如果没有变化,那么文件就可以上传了。但问题是观察者冲突,因为外部程序正在构建文件。
  • 我还能如何判断文件是否准备好?我想在外部程序完成文件制作后自动上传到 youtube。所以我使用标志 while 和 watcher 但在某些情况下 watcher 与外部程序冲突。
  • 也许 IsFileLocked 方法我在 OnChanged 中使用错误?现在我只是打电话给它,但也许我应该用它做点别的?
  • 好的,我将方法更改为 public async Task 所以现在修复了两个错误,但我仍然在初始化 Web 服务器的构造函数中遇到错误: WebServer ws = new WebServer(SendResponse, "http://+:8098/");错误:错误 1 ​​'System.Threading.Tasks.Task Automatic_Record.Form1.SendResponse(System.Net.HttpListenerRequest)' 返回类型错误,这是 WebServer 的方法:public WebServer(Func方法,参数字符串 [] 前缀)我该如何解决这个错误?

标签: c# .net winforms


【解决方案1】:

这段代码被严重破坏了。是的,dirchanged 在 while 循环中始终为 false,因为如果它变为 true,您将不再处于 while 循环中。

此外,您的代码会阻止事件的发生,这可能会阻止文件观察程序事件本身,并且也不是优化安全的。使用适当的同步,这里是一个例子:

TaskCompletionSource<bool> sy;

private void OnChanged(object source, FileSystemEventArgs e)
{
    sy.SetResult(true);
}

然后等待

sy = new TaskCompletionSource<bool>();
WatchDirectory();
await sy.Task; // or sy.Task.Wait()

(您需要在包含该代码的方法上使用 async 关键字)。

这解决了您之前遇到的所有问题——它不会消耗 CPU 周期,它会继续处理 Windows 消息,并且如果编译器选择在寄存器中缓存变量,它也不会中断。

【讨论】:

  • Ben,如果你能告诉我完整的代码应该是什么样子。同时,我正在尝试自己做。谢谢。
  • Ben 我还在我的问题中添加了我刚刚尝试使用异步的内容。不确定我所做的是否是正确的代码,而且我也遇到了一些错误。
【解决方案2】:

dirchanged 可以在评估内部 if 块之后设置为 true。然后,下一个循环它在没有运行你的上传器的情况下爆发。

【讨论】:

    【解决方案3】:

    所以你有两个主要问题?

    1.) 为什么 dirchanged 没有设置为 true?

    以及明显的原因......

    2.) 如何使用 FileSystemWatcher 仅对可编辑的文件执行操作?

    FileSystemWatcher 以有点敏感而闻名,我同意您的诊断,即文件访问可能是罪魁祸首。一个不可预知的文件访问错误正是我对 FileSystemWatcher 试图对刚刚修改的文件执行某些操作所期望的。您可以编辑创建文件的代码吗?如果是这样,我对 FileSystemWatcher 使用的一种方法是让它只监视虚构文件类型的文件创建,例如“.fsw”。创建文件的程序将在完成编辑后将其重命名为“.fsw”,这样 FileSystemWatcher 只有在有文件可供操作时才会被调用,然后它可以将文件重命名为它是实际类型。此外,如果您可以编辑创建代码,请确保您正在尽一切可能从那里发布文件。我以前见过这种行为,因为我忘记关闭 TextWriter。

    另外,我会移动这条线

     dirchanged = true;
    

    在 try 语句之外。为什么要把它放在那里,因为它肯定不会抛出错误?此外,您的 catch 语句并没有真正进行错误处理。 try 语句中的任何错误,并且您在到达 dirchanged = true 行之前被弹出,而不会被警告这就是发生的事情。你自己测试过你的委托代码吗?是否需要在此处更改 type = 的 if 语句?如果您正在排除故障,我会考虑限制您的 try 语句内容或尽可能将其移至 while 循环之后。

    另外,这对于您的 while 语句来说不是更简单吗?

    while (dirchanged == false){}
    Youtube_Uploader youtubeupload = new Youtube_Uploader(fileforupload);  
    

    这不是最优雅的解决方案,但一种解决方法是等待,如果您知道创建/编辑文件的程序将很快关闭它...

    while (dirchanged == false){}
    System.Threading.Thread.Sleep(1000);
    Youtube_Uploader youtubeupload = new Youtube_Uploader(fileforupload);
    

    编辑:更好的是,使用 Ben Voigt 对 TaskCompletionSource 的建议,而不是使用 while 语句。您仍然需要处理被锁定的文件,但您应该能够在“任务”被标记为已完成之后执行此操作。

    【讨论】:

    • 抱歉,我没有将括号放在单独的行中...整个 while 循环存在于我提供的代码中的一行中。它之后的下一行只会在 while 循环中断时执行。 while 循环用于在不满足条件时暂停代码。
    • Dan 但在我的代码中查看我的问题,我在 dirchanged 的​​ init 为 false 之前将 dirchanged 设置为 false。因此,如果您正在执行 while(dirchanged == false),它将进入内部而不检查 dirchanged 是否为真,这就是为什么我在文件未锁定时执行观察者和 OnChanged 事件将其更改为真的原因。我发现这样做 while(true) 然后在内部检查 if(direchanged == true) 然后在内部使用 break;这工作正常。
    • "while (dirchanged == false){}" 持续评估 dirchanged 的​​值。有了你现在所拥有的,while 语句将继续非常快速地循环。如果在嵌套的 if 语句计算后将 dirchanged 的​​值设置为 true,则循环将不会重新进入自身,并且 if 语句将永远不会被计算为 true。这种情况的症状是您无法预测地到达“ttttt”线,正如您所描述的那样。顺便说一句,这是模拟随机性的一种方法。
    猜你喜欢
    • 2021-03-25
    • 2020-06-07
    • 2015-02-05
    • 2015-06-17
    • 1970-01-01
    • 1970-01-01
    • 2017-12-10
    • 2012-08-13
    • 1970-01-01
    相关资源
    最近更新 更多