最好的方法来调用任何跨线程代码?

我知道这个问题之前已经被问过了,但是我正在寻找一种方法来:

  1. 简化安全交叉线程代码的创build。
  2. 在任何情况下重用此代码(无Windows窗体引用)。

这是我到目前为止,但我想删除Windows窗体引用。 有任何想法吗?

public delegate void SafeInvokeDelegate(System.Action action); public class SafeInvoke { private readonly System.Windows.Forms.Control _threadControl; public SafeInvoke() { _threadControl = new System.Windows.Forms.Control(); } public void Invoke(System.Action action) { if (_threadControl.InvokeRequired) _threadControl.Invoke(new SafeInvokeDelegate(Invoke), new object[] {action}); else if (action != null) action(); } } 

上面的类可以这样使用:

 SafeInvoke _safeInvoker = new SafeInvoke(); void SafeClearItems() { _safeInvoker.Invoke(delegate { listView1.Items.Clear(); }); } 

我将如何删除SafeInvoke类中的System.Windows.Forms.Control,但保持相同的function?

你也可以使用扩展方法和lambdaexpression式来使你的代码更清洁。

 using System.ComponentModel; public static class ISynchronizeInvokeExtensions { public static void InvokeEx<T>(this T @this, Action<T> action) where T : ISynchronizeInvoke { if (@this.InvokeRequired) { @this.Invoke(action, new object[] { @this }); } else { action(@this); } } } 

所以现在你可以在任何ISynchronizeInvoke上使用InvokeEx ,并且能够访问实现类的属性和字段。

 this.InvokeEx(f => f.listView1.Items.Clear()); 

使用ISynchronizeInvoke而不是Control 。 这是Control实现的Invoke/BeginInvoke/EndInvoke/InvokeRequired

另一种方法是使用SynchronizationContext.Current – 这是BackgroundWorker使用的,我相信。

这里是在VB.net中,与Samuel的答案非常相似。 我有四个重载取决于你是否想要一个子程序或function,是否有一个参数。 为更多的参数添加更多的重载是很容易的。 VB.Net能够推断出types。

 Module ISynchronizeInvokeExtensions Public Delegate Function GenericLambdaFunctionWithParam(Of InputType, OutputType)(ByVal input As InputType) As OutputType Private Delegate Function InvokeLambdaFunctionCallback(Of InputType, OutputType)(ByVal f As GenericLambdaFunctionWithParam(Of InputType, OutputType), ByVal input As InputType, ByVal c As System.ComponentModel.ISynchronizeInvoke) As OutputType Public Function InvokeEx(Of InputType, OutputType)(ByVal f As GenericLambdaFunctionWithParam(Of InputType, OutputType), ByVal input As InputType, ByVal c As System.ComponentModel.ISynchronizeInvoke) As OutputType If c.InvokeRequired Then Dim d As New InvokeLambdaFunctionCallback(Of InputType, OutputType)(AddressOf InvokeEx) Return DirectCast(c.Invoke(d, New Object() {f, input, c}), OutputType) Else Return f(input) End If End Function Public Delegate Sub GenericLambdaSubWithParam(Of InputType)(ByVal input As InputType) Public Sub InvokeEx(Of InputType)(ByVal s As GenericLambdaSubWithParam(Of InputType), ByVal input As InputType, ByVal c As System.ComponentModel.ISynchronizeInvoke) InvokeEx(Of InputType, Object)(Function(i As InputType) As Object s(i) Return Nothing End Function, input, c) End Sub Public Delegate Sub GenericLambdaSub() Public Sub InvokeEx(ByVal s As GenericLambdaSub, ByVal c As System.ComponentModel.ISynchronizeInvoke) InvokeEx(Of Object, Object)(Function(i As Object) As Object s() Return Nothing End Function, Nothing, c) End Sub Public Delegate Function GenericLambdaFunction(Of OutputType)() As OutputType Public Function InvokeEx(Of OutputType)(ByVal f As GenericLambdaFunction(Of OutputType), ByVal c As System.ComponentModel.ISynchronizeInvoke) As OutputType Return InvokeEx(Of Object, OutputType)(Function(i As Object) f(), Nothing, c) End Function End Module 

用法(在后台工作中运行):

  InvokeEx(Sub(x As String) Me.Text = x, "foo", Me) 'set form title to foo InvokeEx(AddressOf MsgBox, Me.Text, Me) InvokeEx(Sub() Me.Text &= "!", "foo", Me) 'append "!" to form title InvokeEx(AddressOf MsgBox, Me.Text, Me) Dim s As String = InvokeEx(Function() Me.Text, Me) & "bar" 'get form title to backgorundworker thread InvokeEx(AddressOf MsgBox, s, Me) 'display the string from backgroundworker thread 

这里是塞缪尔的答案,我使用的VB等效代码。 注意到我实际上有两个扩展function,但我必须承认我不知道他们为什么在那里。 我在几年前复制了我的C#版本(也许从这个网站),它有两个扩展function,但是由于什么原因,我不完全理解。 我只是复制它,以及如何使用它,我有一半了解这些复杂的function“引擎盖下”所发生的一切。

 #Const System_ComponentModel = True #Const System_Drawing = False Option Compare Binary Option Explicit On Option Strict On Imports System.Collections Imports System.Runtime.CompilerServices ' for Extension() attribute Imports System.Text #If System_ComponentModel Then Imports System.ComponentModel #End If #If System_Drawing Then Imports System.Drawing #End If Public Module MyExtensions ' other #Region blocks are removed. i use many in my Extensions ' module/class. the below code is only the 2 relevant extension ' for this 'SafeInvoke' functionality. but i use other regions ' such as "String extensions" and "Numeric extensions". i use ' the above System_ComponentModel and System_Drawing compiler ' directives to include or exclude blocks of code that i want ' to either include or exclude in a project, which allows me to ' easily compare my code in one project with the same file in ' other projects to syncronise new changes across projects. ' you can scrap pretty much all the code above, ' but i'm giving it here so you see it in the full context. #Region "ISynchronizeInvoke extensions" #If System_ComponentModel Then <Extension()> Public Function SafeInvoke(Of T As ISynchronizeInvoke, TResult)(isi As T, callFunction As Func(Of T, TResult)) As TResult If (isi.InvokeRequired) Then Dim result As IAsyncResult = isi.BeginInvoke(callFunction, New Object() {isi}) Dim endresult As Object = isi.EndInvoke(result) Return DirectCast(endresult, TResult) Else Return callFunction(isi) End If End Function ''' <summary> ''' This can be used in VB with: ''' txtMyTextBox.SafeInvoke(Sub(d) d.Text = "This is my new Text value.") ''' or: ''' txtMyTextBox.SafeInvoke(Sub(d) d.Text = myTextStringVariable) ''' </summary> ''' <typeparam name="T"></typeparam> ''' <param name="isi"></param> ''' <param name="callFunction"></param> ''' <remarks></remarks> <Extension()> Public Sub SafeInvoke(Of T As ISynchronizeInvoke)(isi As T, callFunction As Action(Of T)) If isi.InvokeRequired Then isi.BeginInvoke(callFunction, New Object() {isi}) Else callFunction(isi) End If End Sub #End If #End Region ' other #Region blocks are removed from here too. End Module 

而C#版本是:

 #define System_ComponentModel #undef System_Drawing using System; using System.Collections.Generic; using System.Linq; using System.Text; #if System_ComponentModel using System.ComponentModel; #endif #if System_Drawing using System.Drawing; #endif namespace MyCompany.Extensions { static partial class MyExtensions { // other #Region blocks are removed. i use many in my Extensions // module/class. the below code is only the 2 relevant extension // for this 'SafeInvoke' functionality. but i use other regions // such as "String extensions" and "Numeric extensions". i use // the above System_ComponentModel and System_Drawing compiler // directives to include or exclude blocks of code that i want // to either include or exclude in a project, which allows me to // easily compare my code in one project with the same file in // other projects to syncronise new changes across projects. // you can scrap pretty much all the code above, // but i'm giving it here so you see it in the full context. #region ISynchronizeInvoke extensions #if System_ComponentModel public static TResult SafeInvoke<T, TResult>(this T isi, Func<T, TResult> callFunction) where T : ISynchronizeInvoke { if (isi.InvokeRequired) { IAsyncResult result = isi.BeginInvoke(callFunction, new object[] { isi }); object endResult = isi.EndInvoke(result); return (TResult)endResult; } else return callFunction(isi); } /// <summary> /// This can be used in C# with: /// txtMyTextBox.SafeInvoke(d => d.Text = "This is my new Text value."); /// or: /// txtMyTextBox.SafeInvoke(d => d.Text = myTextStringVariable); /// </summary> /// <typeparam name="T"></typeparam> /// <param name="isi"></param> /// <param name="callFunction"></param> public static void SafeInvoke<T>(this T isi, Action<T> callFunction) where T : ISynchronizeInvoke { if (isi.InvokeRequired) isi.BeginInvoke(callFunction, new object[] { isi }); else callFunction(isi); } #endif #endregion // other #Region blocks are removed from here too. } // static class MyExtensions } // namespace 

快乐的编码!

现在一天很容易调用例如说我们想调用一个Label(lblVal)来获得txtVal的值

 lblVal.invoke((MethodInvoker)delegate{txtVal.Text = lblVal.Text;}); 

那么简单:D