【发布时间】:2011-09-08 21:32:29
【问题描述】:
我正在测试线程的想法,但现在只在非常关键的地方。线程几乎为任何事情增加了相当迷人的复杂性,但在 .NET 中,System.Threading 中的线程似乎有很多选择。我想知道哪个最适合处理字符串操作。
考虑将 complex 字符串提供给自定义对象。该对象当前在某个点拆分字符串,并将第一部分提供给函数,然后当该函数完成时,将字符串的另一半提供给第二个函数。这两个函数相互之间没有依赖关系,因此应该是线程的良好候选者,以便两个函数可以在字符串的每一段上同时工作。
添加前的示例:
Public Sub ParseString(ByVal SomeStr As String)
If String.IsNullOrWhitespace(SomeStr) Then
Throw New ArgumentNullException("SomeStr")
End If
' Assume that ParsedFirstString is a boolean that is set to
' True if the call to ParseFirstString completes successfully.
' Ditto for ParsedSecondString.
Dim MyDelimiter As Char = "|"c
Dim SomeStrArr As String() = SomeStr.Split({MyDelimiter}, 2)
Call Me.ParseFirstString(SomeStrArr(0))
If Me.ParsedFirstString = False Then
Throw New ArgumentException("Failed to parse the first part of the string.")
End If
Call Me.ParseSecondString(SomeStrArr(1))
If Me.ParsedSecondString = False Then
Throw New ArgumentException("Failed to parse the second part of the string.")
End If
End Sub
这工作正常,并且在我的多核系统上的时序循环中进行测试,我可以在 ~140ms-170ms 内执行 1,000 次(如果 10,000 次,则平均 ~1,200ms+)。这是一个可以接受的速度,如果我不能让线程发挥得很好,那么我会继续前进。但是我在查看了一个threading example 和一个关于调用thread with parameters 的SO 问题后尝试了一种线程方法,并最终得到类似于以下的代码:
Public Sub ParseString(ByVal SomeStr As String)
If String.IsNullOrWhitespace(SomeStr) Then
Throw New ArgumentNullException("SomeStr")
End If
Dim MyDelimiter As Char = "|"c
Dim SomeStrArr As String() = SomeStr.Split({MyDelimiter}, 2)
Dim FirstThread As New Thread(Sub() Me.ParseFirstString(SomeStrArr(0))
Dim SecondThread As New Thread(Sub() Me.ParseSecondString(SomeStrArr(1))
FirstThread.Priority = ThreadPriority.Highest
SecondThread.Priority = ThreadPriority.Highest
Call FirstThread.Start()
Call SecondThread.Start()
If Me.ParsedFirstString = False Then
Throw New ArgumentException("Failed to parse the first part of the string.")
End If
If Me.ParsedSecondString = False Then
Throw New ArgumentException("Failed to parse the second part of the string.")
End IF
End Sub
这样做的问题是,字符串的第一部分或第二部分的解析可以在 两者 完成之前完成,这会导致两个异常之一.于是我进一步环顾四周,发现我可以使用Join 方法来等待两个线程都完成。这解决了异常的触发,但它大大增加了执行时间。执行上述函数 1,000 次并对其计时,现在平均运行时间可达约 3,700 毫秒。线程似乎不适合这种任务。
但似乎还有其他线程机制,包括 ThreadPools 和 BackgroundWorkers。可能其他人我还没有查过(我几个小时前才开始搞砸这个)。
社区对此类任务的线程有何看法?我第一次尝试线程有什么问题?
仅供参考,我不会更新任何 UI 组件,也不会将结果写入任何类型的存储介质。
结论:
看来我的字符串解析功能比我想象的要好得多。在尝试了Parallel Class 和Task Class 之后,如果我测试一个 10,000 次迭代循环,然后是单线程,我的测试数据大约为 ~1,220ms-1,260ms。如果我什至实现基本的Parallel.Invoke() 以将解析拆分为两个并行线程,我会将计时循环填充到额外的〜300ms(可能是由于匿名委托的开销,但似乎没有办法解决这个问题)。这是在 Core2 Q9550 Yorkfield,未超频的 95W 处理器上进行比较。
成功的选择是在这个特定的代码区域保持单线程。感谢所有参与的人!
【问题讨论】:
-
第一课:线程并不简单,尤其是看起来很简单的时候。
-
@John:我知道。我之前曾尝试为 SMP 系统编写 IRQ 处理程序。最终结果(到目前为止)并没有取得太大的成功,所以我暂时休息一下并切换回 .NET 开发。
-
在这种情况下,你已经准备好学习线程的第二课了:除非你必须这样做,否则不要这样做。让您的程序在没有线程的情况下工作。如果您随后需要线程来使其表现良好,那么请使用 .NET 的新 TPL 功能来重构您的代码 - 这样它就可以继续工作,但也表现良好。
-
@John:实际上,该项目的框架基本上是完整的。我只是在整理和重新审视我 6 个多月前写的东西。这也允许尝试新事物以查看它们是否有效。如果没有,那很好。仔细阅读我的问题——我说我只是在试验,如果线程不适合我,也没有坏处,没有犯规:)
-
在 Fx4 中,使用任务 (TPL)。 TPL 使用线程池。 Backgroundworker 只能在 GUI (Win/WPF) 中有限使用。
标签: .net vb.net multithreading parallel-processing task-parallel-library