Лучший способ вызвать любой многопоточный код?

38

Я знаю, что этот вопрос задавали раньше, но я ищу способ:

  • упростить создание безопасного кода с поперечной резьбой.
  • повторно использовать этот код в любой ситуации (без ссылок на Windows Forms).

Вот что я до сих пор, но хочу удалить ссылки Windows Forms. Любые идеи?

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();
        });
}

Как удалить System.Windows.Forms.Control в классе SafeInvoke, но сохранить те же функции?

  • 0
    Обратите внимание, что документы, связанные с Invoke () в Control, на самом деле довольно тонкие. Я не верю, что универсального класса достаточно для Control из-за взаимодействия с IsHandleCreated и IsDisposed (если только вы не всегда проверяете их сначала в SafeInvokeDelegate). ( stackoverflow.com/questions/714666 )
  • 0
    Спасибо, что поделились этим классом. Помог мне решить мои проблемы ..
Теги:
invoke

5 ответов

89
Лучший ответ

Вы также можете использовать метод расширения и лямбда, чтобы сделать ваш код намного чище.

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);
    }
  }
}

Итак, теперь вы можете использовать InvokeEx для любого ISynchronizeInvoke и иметь доступ к свойствам и полям реализуемого класса.

this.InvokeEx(f => f.listView1.Items.Clear());
  • 1
    Это может показаться очевидным, но вам также необходимо добавить пространство имен «Система»
  • 0
    Это потрясающая идея. Я сделал это в VB.net, а также 4 перегрузки для подпрограмм / функций / WithParams / WithoutParams. VB.net может выводить универсальные типы.
Показать ещё 6 комментариев
10

Используйте ISynchronizeInvoke вместо Control. Интерфейс, который Control реализует с помощью Invoke/BeginInvoke/EndInvoke/InvokeRequired.

Альтернативой является использование SynchronizationContext.Current - это то, что используется BackgroundWorker.

  • 0
    Не могли бы вы показать пример кода? :-) Реализация ISynchronizeInvoke требует BeginInvoke и т. Д., И может стать утомительной.
  • 0
    Похоже, ISynchronizeInvoke реализован только классом Control. Это не похоже на способ избавиться от зависимости Windows Forms.
Показать ещё 3 комментария
7

В наше время легко вызвать.
например, скажем, мы хотим вызвать Label (lblVal), чтобы получить значение txtVal

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

... это так просто: D

  • 1
    Какое замечательное, простое решение. Спасибо
  • 0
    Это работает отлично. Просто необходимо , чтобы капитализировать invoke -> Invoke .
4

Вот он на VB.net, очень похожий на Самуила. У меня есть четыре перегрузки в зависимости от того, хотите ли вы подпрограмму или функцию и есть ли там параметр. Было бы легко добавить больше перегрузок для большего количества параметров. VB.Net способен вывести типы.

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
  • 0
    Привет. Я использовал ваш код с большим успехом, но я тестировал свою программу на нескольких компьютерах. Он корректно работал на большинстве компьютеров с Win XP, на которых я его тестировал, но на одном из них я получаю следующую ошибку: «Commom Language Runtime обнаружил недопустимую программу». Ты знаешь как это решить? Я использую .NET Framework V2.
  • 0
    Попробуйте обновить CLR? Я думаю, что Sub lambda поддерживается только .Net 4, до этого там была только функция lambda.
Показать ещё 3 комментария
3

здесь эквивалентный код VB для ответа Сэмюэля, который я использую. обратите внимание, что на самом деле у меня есть 2 расширения функций, но я должен признать, что я не знаю, почему они там. Я скопировал мою версию С# несколько лет назад (возможно, с этого сайта), и у нее были функции расширения, но по какой-то причине я не совсем понимаю. я просто скопировал его и как его использовать, и я понимаю, что все это происходит под "капюшоном" с этими сложными функциями.

#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

И версия С#:

#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

Счастливое кодирование!

  • 1
    Очень полезно; особенно расширение, которое работает с возвращаемым результатом. +1
  • 0
    спасибо, я тоже. :П

Ещё вопросы

Сообщество Overcoder
Наверх
Меню