Programing

InvokeRequired 코드 패턴 자동화

lottogame 2020. 5. 22. 08:07
반응형

InvokeRequired 코드 패턴 자동화


이벤트 중심 GUI 코드에서 다음 코드 패턴을 얼마나 자주 작성해야하는지 알고 있습니다.

private void DoGUISwitch() {
    // cruisin for a bruisin' through exception city
    object1.Visible = true;
    object2.Visible = false;
}

된다 :

private void DoGUISwitch() {
    if (object1.InvokeRequired) {
        object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
    } else {
        object1.Visible = true;
        object2.Visible = false;
    }
}

이것은 C #에서 기억하기 쉽고 입력하기에 불편한 패턴입니다. 누구든지 이것을 어느 정도 자동화하는 지름길이나 구성을 생각해 냈습니까? object1.InvokeIfNecessary.visible = true유형 바로 가기 와 같이이 모든 추가 작업을 거치지 않고이 검사를 수행하는 객체에 함수를 첨부하는 방법이 있다면 멋질 것 입니다.

이전 답변 에서는 매번 Invoke ()를 호출하는 것의 비현실성에 대해 논의했으며, 심지어 Invoke () 구문도 비효율적이며 여전히 다루기가 어려워졌습니다.

그래서 지름길을 알아 낸 사람이 있습니까?


이의 접근 방식은 더 단순화 될 수있다

public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
    // See Update 2 for edits Mike de Klerk suggests to insert here.

    if (control.InvokeRequired) {
        control.Invoke(action);
    } else {
        action();
    }
}

그리고 이렇게 불릴 수 있습니다

richEditControl1.InvokeIfRequired(() =>
{
    // Do anything you want with the control here
    richEditControl1.RtfText = value;
    RtfHelpers.AddMissingStyles(richEditControl1);
});

컨트롤을 매개 변수로 대리자에게 전달할 필요가 없습니다. C #은 자동으로 클로저를 생성합니다 .


업데이트 :

다른 여러 포스터에 따르면 다음 Control과 같이 일반화 할 수 있습니다 ISynchronizeInvoke.

public static void InvokeIfRequired(this ISynchronizeInvoke obj,
                                         MethodInvoker action)
{
    if (obj.InvokeRequired) {
        var args = new object[0];
        obj.Invoke(action, args);
    } else {
        action();
    }
}

DonBoitnott는 달리 지적 인터페이스의 오브젝트 배열 필요 위한 파라미터리스트로서 방법 .ControlISynchronizeInvokeInvokeaction


업데이트 2

Mike de Klerk가 제안한 편집 (삽입 점은 첫 번째 코드 스 니펫의 주석 참조) :

// When the form, thus the control, isn't visible yet, InvokeRequired  returns false,
// resulting still in a cross-thread exception.
while (!control.Visible)
{
    System.Threading.Thread.Sleep(50);
}

이 제안에 대한 우려는 아래 ToolmakerSteve의 의견을 참조하십시오.


확장 방법을 작성할 수 있습니다.

public static void InvokeIfRequired(this Control c, Action<Control> action)
{
    if(c.InvokeRequired)
    {
        c.Invoke(new Action(() => action(c)));
    }
    else
    {
        action(c);
    }
}

그리고 이것을 다음과 같이 사용하십시오 :

object1.InvokeIfRequired(c => { c.Visible = true; });

편집 : Simpzon이 주석에서 지적한 것처럼 서명을 다음과 같이 변경할 수도 있습니다.

public static void InvokeIfRequired<T>(this T c, Action<T> action) 
    where T : Control

다음은 모든 코드에서 사용한 양식입니다.

private void DoGUISwitch()
{ 
    Invoke( ( MethodInvoker ) delegate {
        object1.Visible = true;
        object2.Visible = false;
    });
} 

I've based this on the blog entry here. I have not had this approach fail me, so I see no reason to complicate my code with a check of the InvokeRequired property.

Hope this helps.


Create a ThreadSafeInvoke.snippet file, and then you can just select the update statements, right click and select 'Surround With...' or Ctrl-K+S:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <Header>
    <Title>ThreadsafeInvoke</Title>
    <Shortcut></Shortcut>
    <Description>Wraps code in an anonymous method passed to Invoke for Thread safety.</Description>
    <SnippetTypes>
      <SnippetType>SurroundsWith</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Code Language="CSharp">
      <![CDATA[
      Invoke( (MethodInvoker) delegate
      {
          $selected$
      });      
      ]]>
    </Code>
  </Snippet>
</CodeSnippet>

Here's an improved/combined version of Lee's, Oliver's and Stephan's answers.

public delegate void InvokeIfRequiredDelegate<T>(T obj)
    where T : ISynchronizeInvoke;

public static void InvokeIfRequired<T>(this T obj, InvokeIfRequiredDelegate<T> action)
    where T : ISynchronizeInvoke
{
    if (obj.InvokeRequired)
    {
        obj.Invoke(action, new object[] { obj });
    }
    else
    {
        action(obj);
    }
} 

The template allows for flexible and cast-less code which is much more readable while the dedicated delegate provides efficiency.

progressBar1.InvokeIfRequired(o => 
{
    o.Style = ProgressBarStyle.Marquee;
    o.MarqueeAnimationSpeed = 40;
});

I'd rather use a single instance of a method Delegate instead of creating a new instance every time. In my case i used to show progress and (info/error) messages from a Backroundworker copying and casting large data from a sql instance. Everywhile after about 70000 progress and message calls my form stopped working and showing new messages. This didn't occure when i started using a single global instance delegate.

delegate void ShowMessageCallback(string message);

private void Form1_Load(object sender, EventArgs e)
{
    ShowMessageCallback showMessageDelegate = new ShowMessageCallback(ShowMessage);
}

private void ShowMessage(string message)
{
    if (this.InvokeRequired)
        this.Invoke(showMessageDelegate, message);
    else
        labelMessage.Text = message;           
}

void Message_OnMessage(object sender, Utilities.Message.MessageEventArgs e)
{
    ShowMessage(e.Message);
}

Usage:

control.InvokeIfRequired(c => c.Visible = false);

return control.InvokeIfRequired(c => {
    c.Visible = value

    return c.Visible;
});

Code:

using System;
using System.ComponentModel;

namespace Extensions
{
    public static class SynchronizeInvokeExtensions
    {
        public static void InvokeIfRequired<T>(this T obj, Action<T> action)
            where T : ISynchronizeInvoke
        {
            if (obj.InvokeRequired)
            {
                obj.Invoke(action, new object[] { obj });
            }
            else
            {
                action(obj);
            }
        }

        public static TOut InvokeIfRequired<TIn, TOut>(this TIn obj, Func<TIn, TOut> func) 
            where TIn : ISynchronizeInvoke
        {
            return obj.InvokeRequired
                ? (TOut)obj.Invoke(func, new object[] { obj })
                : func(obj);
        }
    }
}

I Kind of like to do it a bit different, i like to call "myself" if needed with an Action,

    private void AddRowToListView(ScannerRow row, bool suspend)
    {
        if (IsFormClosing)
            return;

        if (this.InvokeRequired)
        {
            var A = new Action(() => AddRowToListView(row, suspend));
            this.Invoke(A);
            return;
        }
         //as of here the Code is thread-safe

this is a handy pattern, the IsFormClosing is a field that i set to True when I am closing my form as there might be some background threads that are still running...


You should never be writing code that looks like this:

private void DoGUISwitch() {
    if (object1.InvokeRequired) {
        object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
    } else {
        object1.Visible = true;
        object2.Visible = false;
    }
}

If you do have code that looks like this then your application is not thread-safe. It means that you have code which is already calling DoGUISwitch() from a different thread. It's too late to be checking to see if it's in a different thread. InvokeRequire must be called BEFORE you make a call to DoGUISwitch. You should not access any method or property from a different thread.

Reference: Control.InvokeRequired Property where you can read the following:

In addition to the InvokeRequired property, there are four methods on a control that are thread safe to call: Invoke, BeginInvoke, EndInvoke and CreateGraphics if the handle for the control has already been created.

In a single CPU architecture there's no problem, but in a multi-CPU architecture you can cause part of the UI thread to be assigned to the processor where the calling code was running...and if that processor is different from where the UI thread was running then when the calling thread ends Windows will think that the UI thread has ended and will kill the application process i.e. your application will exit without error.

참고URL : https://stackoverflow.com/questions/2367718/automating-the-invokerequired-code-pattern

반응형