Cross-Thread-Betrieb nicht gültig: Control von einem Thread zugegriffen andere als das Gewinde der es erstellt wurde

StackOverflow https://stackoverflow.com/questions/142003

Frage

Ich habe ein Szenario. (Windows Forms, C #, .NET)

  1. Es gibt eine Hauptform, die eine Benutzersteuerung ausrichtet.
  2. Die Benutzersteuerung hat einige schwere Datenoperation, so dass, wenn ich die UserControl_Load Methode für die Dauer für Load-Methode Ausführung nonresponsive werden die Benutzeroberfläche direkt aufrufen.
  3. Um diese I Laden von Daten auf anderen Thread zu überwinden (versuchen vorhandenen Code zu ändern, so wenig wie ich kann)
  4. habe ich einen Hintergrund Worker-Thread, der die Daten werden geladen und wenn die Anwendung benachrichtigt getan, dass sie ihre Arbeit verrichtet hat.
  5. kam nun ein echtes Problem. All die UI (Hauptform und sein Kind Benutzersteuerelemente) auf dem primären Hauptthread erzeugt. In der LOAD-Methode des Usercontrol bin ich Abrufdaten, basierend auf den Werten von einem gewissen Kontrolle (wie Textbox) auf Usercontrol.

Der Pseudo-Code würde wie folgt aussehen:

CODE 1

UserContrl1_LoadDataMethod()
{
    if (textbox1.text == "MyName") // This gives exception
    {
        //Load data corresponding to "MyName".
        //Populate a globale variable List<string> which will be binded to grid at some later stage.
    }
}

Die Ausnahme es gab, war

  

Cross-Thread-Betrieb nicht gültig. Steuerung von einem Thread zugegriffen andere als das Gewinde der es erstellt wurde

Um mehr darüber zu wissen, habe ich einige googeln und eine Anregung, wie mit dem folgenden Code aufkam

CODE 2

UserContrl1_LoadDataMethod()
{
    if (InvokeRequired) // Line #1
    {
        this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));
        return;
    }

    if (textbox1.text == "MyName") // Now it wont give an exception
    {
    //Load data correspondin to "MyName"
        //Populate a globale variable List<string> which will be binded to grid at some later stage
    }
}

ABER ABER ABER ... es scheint, ich bin wieder auf Platz eins. Die Anwendung wieder wird nicht mehr reagiert. Es scheint die Ausführung von Zeile # 1, wenn die Bedingung zurückzuführen ist. Die Lade Aufgabe wird wieder von dem Muttergewinde getan und nicht die dritte, dass ich hervorgebracht.

Ich weiß nicht, ob ich das richtig wahrgenommen oder falsch. Ich bin neu in Threading.

Wie löse ich diese und auch das, was die Wirkung der Ausführung der Linie # 1, wenn Block?

Die Situation ist dies : Ich möchte Daten in eine globale Variable auf dem Wert eines Steuer basiert laden. Ich will nicht den Wert einer Steuerung von dem Kind Thread ändern. Ich werde es nicht tun jemals von einem Kind Faden.

Zugriff So nur den Wert, so dass die entsprechenden Daten aus der Datenbank abgerufen werden.

War es hilfreich?

Lösung

Wie pro Prerak K-Update Kommentar (seit gelöscht):

  

Ich glaube, ich habe die Frage nicht richtig dargestellt.

     

Situation ist: Ich möchte Daten in eine globale Variable auf dem Wert eines Steuer basiert laden. Ich will nicht den Wert einer Steuerung von dem Kind Thread ändern. Ich bin es nicht immer von einem Kind Thread tun wird.

     

Zugriff So nur den Wert, so dass entsprechende Daten aus der Datenbank abgerufen werden können.

Die Lösung, die Sie dann wollen aussehen sollte:

UserContrl1_LOadDataMethod()
{
    string name = "";
    if(textbox1.InvokeRequired)
    {
        textbox1.Invoke(new MethodInvoker(delegate { name = textbox1.text; }));
    }
    if(name == "MyName")
    {
        // do whatever
    }
}

Haben Ihre ernsthafte Verarbeitung im eigenen Thread vor Sie versuchen, die Kontrolle des Thread zu wechseln. Zum Beispiel:

UserContrl1_LOadDataMethod()
{
    if(textbox1.text=="MyName") //<<======Now it wont give exception**
    {
        //Load data correspondin to "MyName"
        //Populate a globale variable List<string> which will be
        //bound to grid at some later stage
        if(InvokeRequired)
        {
            // after we've done all the processing, 
            this.Invoke(new MethodInvoker(delegate {
                // load the control with the appropriate data
            }));
            return;
        }
    }
}

Andere Tipps

Threading-Modell in UI

Bitte lesen Sie die Threadingmodell grundlegenden Konzepte zu verstehen. Der Link navigiert zu Seite, die das WPF Threading-Modell beschreibt. Allerdings nutzt Windows Forms die gleiche Idee.

Der UI-Thread

  • Es ist nur ein Thread (UI-Thread), die den Zugriff auf System.Windows.Forms.Control und ihre Unterklassen Mitglieder.
  • Versuchen Sie für den Zugriff auf Mitglied von System.Windows.Forms von anderen Thread .Control als UI-Thread verkanten Ausnahme verursacht.
  • Da gibt es nur ein Thread ist, werden alle UI-Operationen als Workitems in diesem Thread die Warteschlange gestellt werden:

eingeben Bild Beschreibung hier

Sie wollen nur Invoke oder BeginInvoke für das Nötigste Stück Arbeit verwenden, erforderlich, um die Benutzeroberfläche zu ändern. Ihr „schweres“ Verfahren auf einem anderen Thread (beispielsweise über Background) ausführen soll, aber dann Control.Invoke / Control.BeginInvoke nur mit dem UI zu aktualisieren. Auf diese Weise Ihren UI-Thread wird frei sein UI-Ereignisse zu behandeln etc.

Sehen Sie mein rel="noreferrer"> Threading für eine

Ich habe dieses Problem mit dem FileSystemWatcher hatte und festgestellt, dass der folgende Code das Problem gelöst:

fsw.SynchronizingObject = this

Die Steuerung verwendet dann das aktuelle Formularobjekt mit den Ereignissen zu befassen, und wird daher auf dem gleichen Thread sein.

Ich weiß, dass es zu spät jetzt. Doch auch heute, wenn Sie Probleme beim Zugriff auf Querfaden Kontrollen haben? Dies ist die kürzeste Antwort bis heute: P

Invoke(new Action(() =>
                {
                    label1.Text = "WooHoo!!!";
                }));

Dies ist, wie ich Zugriff auf jede Form Steuerung von einem Thread.

Steuerelemente in .NET sind im Allgemeinen nicht Thread-sicher. Das bedeutet, dass Sie keine Kontrolle von einem anderen Thread als dem, in dem sie lebt zugreifen soll. Um dies zu umgehen, müssen Sie invoke die Kontrolle, das ist, was Ihre zweite Probe versucht.

Doch in Ihrem Fall alles, was Sie getan haben, ist die lang andauernden Verfahren zurück zum Haupt-Thread übergeben. Natürlich, das ist nicht wirklich das, was Sie tun mögen. Sie müssen diese ein wenig zu überdenken, so dass alles, was Sie auf dem Hauptthread tun ist eine schnelle Eigenschaft hier und dort zu setzen.

Ich finde den Check-and-invoke-Code, der auf Formen, die im Rahmen aller Methoden übersät werden muss, um seinen Weg zu ausführlich und nicht mehr benötigt. Hier ist eine einfache Erweiterung Methode, die Sie damit machen weg lässt vollständig:

public static class Extensions
{
    public static void Invoke<TControlType>(this TControlType control, Action<TControlType> del) 
        where TControlType : Control
        {
            if (control.InvokeRequired)
                control.Invoke(new Action(() => del(control)));
            else
                del(control);
    }
}

Und dann können Sie einfach tun:

textbox1.Invoke(t => t.Text = "A");

Nicht mehr Herumspielen -. Einfach

Ein neuer Look mit Async / Await und Rückrufe. Sie benötigen nur eine Zeile Code, wenn Sie die Erweiterungsmethode in Ihrem Projekt halten.

/// <summary>
/// A new way to use Tasks for Asynchronous calls
/// </summary>
public class Example
{
    /// <summary>
    /// No more delegates, background workers etc. just one line of code as shown below
    /// Note it is dependent on the XTask class shown next.
    /// </summary>
    public async void ExampleMethod()
    {
        //Still on GUI/Original Thread here
        //Do your updates before the next line of code
        await XTask.RunAsync(() =>
        {
            //Running an asynchronous task here
            //Cannot update GUI Thread here, but can do lots of work
        });
        //Can update GUI/Original thread on this line
    }
}

/// <summary>
/// A class containing extension methods for the Task class 
/// Put this file in folder named Extensions
/// Use prefix of X for the class it Extends
/// </summary>
public static class XTask
{
    /// <summary>
    /// RunAsync is an extension method that encapsulates the Task.Run using a callback
    /// </summary>
    /// <param name="Code">The caller is called back on the new Task (on a different thread)</param>
    /// <returns></returns>
    public async static Task RunAsync(Action Code)
    {
        await Task.Run(() =>
        {
            Code();
        });
        return;
    }
}

Sie können andere Dinge auf die Erweiterung Methode hinzufügen, wie es in einer Try / Catch-Anweisung Verpackung, so dass Anrufer es zu sagen, welche Art nach Abschluss zurückzukehren, wird eine Ausnahme Rückruf Anrufer:

Hinzufügen von Try-Catch, Auto Ausnahme Protokollierung und Callback

    /// <summary>
    /// Run Async
    /// </summary>
    /// <typeparam name="T">The type to return</typeparam>
    /// <param name="Code">The callback to the code</param>
    /// <param name="Error">The handled and logged exception if one occurs</param>
    /// <returns>The type expected as a competed task</returns>

    public async static Task<T> RunAsync<T>(Func<string,T> Code, Action<Exception> Error)
    {
       var done =  await Task<T>.Run(() =>
        {
            T result = default(T);
            try
            {
               result = Code("Code Here");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Unhandled Exception: " + ex.Message);
                Console.WriteLine(ex.StackTrace);
                Error(ex);
            }
            return result;

        });
        return done;
    }
    public async void HowToUse()
    {
       //We now inject the type we want the async routine to return!
       var result =  await RunAsync<bool>((code) => {
           //write code here, all exceptions are logged via the wrapped try catch.
           //return what is needed
           return someBoolValue;
       }, 
       error => {

          //exceptions are already handled but are sent back here for further processing
       });
        if (result)
        {
            //we can now process the result because the code above awaited for the completion before
            //moving to this statement
        }
    }

Sie müssen am Beispiel aussehen Backgroundworker:
http://msdn.microsoft.com/en-us/library /system.componentmodel.backgroundworker.aspx Speziell, wie in Wechselwirkung mit der UI-Ebene. Basierend auf Ihrem Beitrag, scheint dies Ihre Fragen zu beantworten.

die einfachste (meiner Meinung nach) Folgen Weg, um Objekte von einem anderen Thread zu ändern:

using System.Threading.Tasks;
using System.Threading;

namespace TESTE
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Action<string> DelegateTeste_ModifyText = THREAD_MOD;
            Invoke(DelegateTeste_ModifyText, "MODIFY BY THREAD");
        }

        private void THREAD_MOD(string teste)
        {
            textBox1.Text = teste;
        }
    }
}

fand ich eine Notwendigkeit für diesen während der Programmierung ein iOS-Phone App Controller Monotouch in einem Visual Studio winforms Prototyp Projekt außerhalb von Xamarin stuidio. Liebes Programm in VS über Xamarin Studio so viel wie möglich, wollte ich die Steuerung vollständig aus dem Telefon Rahmen entkoppelt werden. Auf diese Weise diese für andere Frameworks wie Android und Windows Phone Implementierung wäre viel einfacher für zukünftige Anwendungen.

wollte ich eine Lösung, bei der die GUI auf Ereignisse ohne die Last des Umgangs mit dem Kreuz Threading Schaltcode hinter jedem Tastenklick reagieren könnte. Grundsätzlich lassen die Klasse Controller Griff, der den Client-Code einfach zu halten. Sie könnten möglicherweise viele Ereignisse auf der GUI, wo, als ob Sie es an einem Ort in der Klasse sauberer wären umgehen konnten. Ich bin kein Multi theading Experte, lassen Sie mich wissen, ob dies fehlerhaft ist.

public partial class Form1 : Form
{
    private ExampleController.MyController controller;

    public Form1()
    {          
        InitializeComponent();
        controller = new ExampleController.MyController((ISynchronizeInvoke) this);
        controller.Finished += controller_Finished;
    }

    void controller_Finished(string returnValue)
    {
        label1.Text = returnValue; 
    }

    private void button1_Click(object sender, EventArgs e)
    {
        controller.SubmitTask("Do It");
    }
}

Die GUI-Form ist nicht bewusst, die Steuerung asynchrone Aufgaben ausgeführt wird.

public delegate void FinishedTasksHandler(string returnValue);

public class MyController
{
    private ISynchronizeInvoke _syn; 
    public MyController(ISynchronizeInvoke syn) {  _syn = syn; } 
    public event FinishedTasksHandler Finished; 

    public void SubmitTask(string someValue)
    {
        System.Threading.ThreadPool.QueueUserWorkItem(state => submitTask(someValue));
    }

    private void submitTask(string someValue)
    {
        someValue = someValue + " " + DateTime.Now.ToString();
        System.Threading.Thread.Sleep(5000);
//Finished(someValue); This causes cross threading error if called like this.

        if (Finished != null)
        {
            if (_syn.InvokeRequired)
            {
                _syn.Invoke(Finished, new object[] { someValue });
            }
            else
            {
                Finished(someValue);
            }
        }
    }
}

Dies ist nicht der empfohlene Weg, um diesen Fehler zu lösen, aber man kann es schnell zu unterdrücken, es wird die Arbeit machen. Ich ziehe diese für Prototypen oder Demos. addieren

CheckForIllegalCrossThreadCalls = false

in Form1() Konstruktor.

Hier ist eine alternative Art und Weise, wenn das Objekt mit dem Sie arbeiten nicht über

(InvokeRequired)

Dies ist nützlich, wenn Sie mit der Hauptform in einer anderen Klasse als die Hauptform mit einem Objekt arbeiten, die in der Hauptform, aber nicht InvokeRequired hat

delegate void updateMainFormObject(FormObjectType objectWithoutInvoke, string text);

private void updateFormObjectType(FormObjectType objectWithoutInvoke, string text)
{
    MainForm.Invoke(new updateMainFormObject(UpdateObject), objectWithoutInvoke, text);
}

public void UpdateObject(ToolStripStatusLabel objectWithoutInvoke, string text)
{
    objectWithoutInvoke.Text = text;
}

Es funktioniert auf die gleiche wie oben, aber es ist ein anderer Ansatz, wenn Sie nicht über ein Objekt mit InvokeRequired haben aber haben Zugriff auf das Mainform

In der gleichen Richtung wie in früheren Antworten, aber eine sehr kurze Ergänzung, die alle Steuerungseigenschaften verwenden kann, ohne Querfaden Invokation Ausnahme ist.

Helper-Methode

/// <summary>
/// Helper method to determin if invoke required, if so will rerun method on correct thread.
/// if not do nothing.
/// </summary>
/// <param name="c">Control that might require invoking</param>
/// <param name="a">action to preform on control thread if so.</param>
/// <returns>true if invoke required</returns>
public bool ControlInvokeRequired(Control c, Action a)
{
    if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate
    {
        a();
    }));
    else return false;

    return true;
}

Beispiel Verbrauch

// usage on textbox
public void UpdateTextBox1(String text)
{
    //Check if invoke requied if so return - as i will be recalled in correct thread
    if (ControlInvokeRequired(textBox1, () => UpdateTextBox1(text))) return;
    textBox1.Text = ellapsed;
}

//Or any control
public void UpdateControl(Color c, String s)
{
    //Check if invoke requied if so return - as i will be recalled in correct thread
    if (ControlInvokeRequired(myControl, () => UpdateControl(c, s))) return;
    myControl.Text = s;
    myControl.BackColor = c;
}
this.Invoke(new MethodInvoker(delegate
            {
                //your code here;
            }));

Zum Beispiel den Text von einer Steuerung des UI-Thread zu bekommen:

Private Delegate Function GetControlTextInvoker(ByVal ctl As Control) As String

Private Function GetControlText(ByVal ctl As Control) As String
    Dim text As String

    If ctl.InvokeRequired Then
        text = CStr(ctl.Invoke(
            New GetControlTextInvoker(AddressOf GetControlText), ctl))
    Else
        text = ctl.Text
    End If

    Return text
End Function

Gleiche Frage: How-to-Update- the-gui-from-andere-thread-in-c

Zwei Wege:

  1. Rückgabewert in e.result und es verwendet, yout Textbox Wert in backgroundWorker_RunWorkerCompleted Ereignisse

  2. einstellen
  3. eine Variable deklarieren, diese Art von Werten in einer separaten Klasse zu halten (die als Dateninhaber arbeiten). Erstellen Sie statische Instanz dieser Klasse adn Sie es über jeden Thread zugreifen kann.

Beispiel:

public  class data_holder_for_controls
{
    //it will hold value for your label
    public  string status = string.Empty;
}

class Demo
{
    public static  data_holder_for_controls d1 = new data_holder_for_controls();
    static void Main(string[] args)
    {
        ThreadStart ts = new ThreadStart(perform_logic);
        Thread t1 = new Thread(ts);
        t1.Start();
        t1.Join();
        //your_label.Text=d1.status; --- can access it from any thread 
    }

    public static void perform_logic()
    {
        //put some code here in this function
        for (int i = 0; i < 10; i++)
        {
            //statements here
        }
        //set result in status variable
        d1.status = "Task done";
    }
}

Aktion y; // innerhalb Klasse deklariert

label1.Invoke (y = () => label1.Text = "text");

Verwenden Sie einfach diese:

this.Invoke((MethodInvoker)delegate
            {
                YourControl.Property= value; // runs thread safe
            });

Es gibt zwei Möglichkeiten für Cross-Thread-Operationen.

Control.InvokeRequired Property 

und zweite ist die Verwendung

SynchronizationContext Post Method

Control.InvokeRequired nur dann sinnvoll ist, wenn Kontrollen geerbt von Control-Klasse arbeiten, während SynchronizationContext überall eingesetzt werden kann. Einige nützlichen Informationen wie folgende Links

Kreuz Thema Update UI | .Net

Kreuz Thema Update UI SynchronizationContext mit | .Net

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top