如果我们 运行 来自不同进程的 async 和 await 代码表现不同

async and await code behaving differently if we run it from different processes

这个问题很长,请耐心等待。

场景: 我在 root-directory 上有一个 FileSystemWatcher 正在监视 LastWritecontrol-file 上的每个变化控制目录。看起来像这样:-

root-directory (FileSystemWatcher instance is watching this root directory)

|

|-----control-directory_0 \ control-file

                      \ data-file

|-----control-directory_1 \ control-file

                      \ data-file

|-----control-directory_2 \ control-file

                      \ data-file

|-----control-directory_3 \ control-file

                      \ data-file

.........more similar structure (but only one root-directory)

对不起我的创意画。

我遇到的问题,根目录里面可以有很多控制目录(大约200-500)和连同 control-directory 的 control-file,我在其中有一个 data-file(在每个其中)发生连续写入的地方。

我在看 NotifyFilter.LastWriteFilter = control-fileInternalBufferSize 设置为 4KB(请不要要求增加它,由于要求,我不允许修改它和事件使其成为 8KB(默认值),我不能使用 FileSystemWatcher对于每个 control-file 因为它会占用大量宝贵的非分页内存)。

我的 EventHandler 看起来像这样

  private void OnChange(Object sender, FileSystemEventArgs eventArgs)
  {
     //result = CPU bound work here
     //evaluating result
  }

目前,我正在处理 300 个 control-files。由于 data-file 上发生了大量写入,并且 control-file 上有大量请求,我的缓冲区经常溢出。

然后我想到了一个主意。 (asyncawait 使用 Task 主战坦克进行救援)

  async private void OnChange(Object sender, FileSystemEventArgs eventArgs)
  {
     var result = await Task.Run(() => Work.CPUboundWork());
     //evaluating result
  }

它运行良好,我能够处理大量请求(甚至在只有 4KB 缓冲区的情况下测试了 1000 个事件)。

有趣的部分来了。我如何测试它?

涉及两个实体,一是写入 controldata-file,二是处理由写入 控制文件 。所以我上去创建了一个控制台应用程序,它完成了这两件事。

static void Main(string[] args)
{
  int controlFileCount = Convert.ToInt32(args[0]);
  string basePath = args[1];
  //CreateFolderStructure(basePath, controlFileCount); for first run.
  FileSystemWatcher baseDirectoryWatcher = new FileSystemWatcher();
  SetupFileSystemWatcher(baseDirectoryWatcher, basePath);
  WriteToControlFilesParallely(basePath, controlFileCount);
  Console.Read();
}

public void SetupFileSystemWatcher(FileSystemWatcher baseDirectoryWatcher, string path)
{
  //setting properties of the watcher.
  baseDirectoryWatcher.NotifyFilter = NotifyFilter.LastWrite;
  baseDirectoryWatcher.Filter = "control-file";
  baseDirectoryWatcher.InternalBufferSize = 4096;
  baseDirectoryWatcher.Path = path;
  baseDirectoryWatcher.IncludeSubdirectories = true;
  baseDirectoryWatcher.EnableRaisingEvents = true;
}

public void WriteToControlFilesParallely(string basePath, int controlFileCount)
{
  Parallel.For(0, controlFileCount, (i) =>
  {
    string filePath = Helper.GetFilePath(basePath, i);
    Helper.WriteData(filePath, "data");
  });
}

接下来,我用两个控制台应用程序进行了测试:-

首先,负责将数据并行写入各个控制文件- (Writer_App).

二、负责处理事件-(Event_Handling_App).

注意:核心中没有代码更改,但现在我不是在同一个控制台应用程序中编写和处理事件,而是从一个控制台应用程序编写并处理另一个。

所以我将两个 entities 从我的旧控制台应用程序中分离出来(开始对实际环境进行模拟)。现在我先通过 运行 Event_Handling_App 开始测试,然后通过 运行 写入 control-file Writer_App 现在事情变得很奇怪,我的应用程序面临 InternalBufferOverflow 相同数量的 control-files 异常] 以及当我没有 asyncawait 时与我的第一个实现中相同的写入次数。

我再次使用一个控制台应用程序(它同时承担了这两种责任)进行了测试,它运行良好。

So, why two console applications make the magic of async and await (along with Task) disappear while one console application is working great?

Is it something related to Inter Process Communication?

I am wondering if it will work in production because I will be using handler code in a Web application and some other process can write these files over the network.

最后,我能够弄清楚这一点,当我使用单个控制台应用程序时,我 运行 正在使用它的 exe(通过 cmd),但是当我尝试使用两个控制台应用程序时,我是 运行在 visual studio 中安装应用程序(在发布模式下)所以由于这个(负载为 visual studio)我得到了异常,我责怪 asyncawait .

因此,如果您正在处理紧凑的代码,请始终 运行 您的应用程序来自 exe。