【问题标题】:.NET Parallel file creation.NET 并行文件创建
【发布时间】:2013-08-29 13:22:40
【问题描述】:

我正在创建一个从数据库中获取文件数据的程序。要求是文件在存在“IMAGE_00001.JPG”、“IMAGE_00002.JPG”、...

由于性能问题,我正在并行加载数据,但在创建文件时遇到了问题。

For i As Long = 1 To maxSeqNr
    DIDir = New DirectoryInfo(currDir)
    If DIDir.GetFiles(currFilePrefix & Microsoft.VisualBasic.Right(maxSeqNrLeadingZeros & CStr(i), Len(maxSeqNr.ToString)) & ".*", SearchOption.TopDirectoryOnly).Length = 0 Then
        sRetValue = currFilePrefix & Microsoft.VisualBasic.Right(maxSeqNrLeadingZeros & CStr(i), Len(maxSeqNr.ToString)) & currFileExtention

        Dim oSW As StreamWriter
        oSW = New StreamWriter(currDir & sRetValue)
        oSW.WriteLine("")
        oSW.Close()
        oSW.Dispose()
        oSW = Nothing

        Exit For
    End If
Next

问题是有时 2 个线程运行同一行代码。 我添加了日志记录,我发现了这个:

Check for 'IMAGE_00001.JPG' ; Directory: ''
Check for 'IMAGE_00001.JPG' ; Directory: ''
Check for 'IMAGE_00002.JPG' ; Directory: 'IMAGE_00001.JPG'
Check for 'IMAGE_00003.JPG' ; Directory: 'IMAGE_00001.JPG,IMAGE_00002.JPG'

在我执行 Exists 语句之前是否有可能“锁定”工作目录。创建文件后“释放”? 其他线程应该等到他们可以“锁定”目录。

【问题讨论】:

  • 你应该使用data structure which supports a producer/consumer strategy 这样他们就不会检查相同的文件,否则你的性能仍然会很差。
  • @user7116 我不确定你为什么建议使用数据结构?我有一个带有 id 的集合,我获取数据,创建文件并将流写入该文件。我的代码中没有并行保存
  • 你是如何建立文件名的? (sFileName)。这就是问题的症结所在。向我们展示你在做什么。
  • @xanatos 我更新了我的问题
  • @Stinus:因为您最初提供了 0 个有关如何获得文件名的详细信息。您需要 (1) 使用数据结构将文件名分配给各个线程,或者 (2) 使用其他方式(例如 Parallel.For)分配文件名。

标签: .net vb.net multithreading file-io


【解决方案1】:

您应该生成要使用的名称,然后尝试使用该名称创建FileStream 对象。使用接受FileMode 参数的合适构造函数(例如this one)并指定CreateNew。

如果抛出一个IOException,则捕获并生成一个新名称。

否则,您将面临一种经典的比赛形式。您是选择组合原来的策略(检查是否存在,然后尝试打开文件)还是只是尝试创建文件并期待异常,这取决于您。

成功打开FileStream 后,您可以将其传递给接受Stream 参数的StreamWriter 构造函数,然后从那里继续。

【讨论】:

  • 谢谢,我错过了“FileMode.CreateNew”选项。
【解决方案2】:

我会使用 syn/lock 策略: 当您的线程想要写入文件并停止所有其他线程直到完成时锁定,然后释放锁定。 http://msdn.microsoft.com/de-de/library/vstudio/ms173179.aspx

或者,使用非阻塞整数作为计数器,这样可以保证文件的唯一性,但它可能会弄乱您的“编号”要求。 http://msdn.microsoft.com/en-us/library/system.threading.interlocked.aspx

【讨论】:

    【解决方案3】:

    如果您需要做的只是生成空白文件IMG_00000.jpg 到IMG_XXXXX.jpg,如果文件不存在,您可以使用Parallel.For 循环来创建这些文件。这将确保两个数字不会被两个不同的线程检查,而是会在线程之间分割出数字。

    虽然我不知道这实际上会更快,特别是如果您必须在目录中搜索具有与图像名称匹配的任何扩展名的文件:

    Dim width = Len(maxSeqNr.ToString)
    Dim DIDir = New DirectoryInfo(currDir)
    Parallel.For(1L, maxSeqNr, Sub(i)
        Dim pattern = currFilePrefix & Microsoft.VisualBasic.Right(maxSeqNrLeadingZeros & CStr(i), Len(maxSeqNr.ToString)) & ".*"
        If Not DDir.EnumerateFiles(pattern, SearchOption.TopDirectoryOnly).Any Then
            Dim fn = currFilePrefix & Microsoft.VisualBasic.Right(maxSeqNrLeadingZeros & CStr(i), width) & currFileExtention
            File.Create(fn).Close
        End If
    End Sub)
    

    如果您要搜索的文件夹很小,这可能会更好(假设您实际上关心与模式匹配的任何文件名):

    ' My VB.Net is horrible
    Dim width = Len(maxSeqNr.ToString)
    Dim files = Directory.GetFiles(currDir)
    Parallel.For(1L, maxSeqNr, Sub(i)
        Dim pattern = currFilePrefix & Microsoft.VisualBasic.Right(maxSeqNrLeadingZeros & CStr(i), Len(maxSeqNr.ToString))
        ' consider case-insenstive matching
        If Not files.Any(Function(fn) Path.GetFileNameWithoutExtension(fn) = pattern) Then
            Dim fn = currFilePrefix & Microsoft.VisualBasic.Right(maxSeqNrLeadingZeros & CStr(i), width) & currFileExtention
            File.Create(fn).Close
        End If
    End Sub)
    

    或者,如果您真正需要的只是创建一个不存在的文件,您只需继续触摸它们:

    Parallel.For(1L, maxSeqNr, Sub(i)
        Dim fn = currFilePrefix & Microsoft.VisualBasic.Right(maxSeqNrLeadingZeros & CStr(i), width) & currFileExtention
        File.Open(fn, FileMode.OpenOrCreate).Close
    End Sub)
    

    【讨论】:

      猜你喜欢
      • 2016-08-17
      • 1970-01-01
      • 2011-04-16
      • 2010-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-21
      相关资源
      最近更新 更多