1592 Stimmen

Wie aktualisiere ich die GUI von einem anderen Thread aus?

Welches ist der einfachste Weg, eine Label von einem anderen Thread ?

  • Tengo un Form weiterlaufend thread1 und davon ausgehend eröffne ich ein weiteres Thema ( thread2 ).

  • Während thread2 einige Dateien verarbeitet, würde ich gerne eine Label sur le Form mit dem aktuellen Status von thread2 Arbeit.

Wie könnte ich das tun?

28 Stimmen

Hat .net 2.0+ nicht die BackgroundWorker-Klasse genau für diesen Zweck. Es UI Thread bewusst. 1. Erstellen Sie einen BackgroundWorker 2. Fügen Sie zwei Delegierte hinzu (einen für die Verarbeitung und einen für den Abschluss)

15 Stimmen

4 Stimmen

Siehe die Antwort für .NET 4.5 und C# 5.0: stackoverflow.com/a/18033198/2042090

6voto

Roman Ambinder Punkte 359

Grundsätzlich kann dieses Problem unabhängig von der Framework-Version oder der zugrundeliegenden GUI-Bibliothek gelöst werden, indem man das Steuerelement speichert, indem man den Synchronisationskontext des Threads für den Worker-Thread erstellt, der die mit dem Steuerelement verbundene Interaktion vom Worker-Thread in die Thread-Nachrichtenwarteschlange des GUI einspeist.

Beispiel:

SynchronizationContext ctx = SynchronizationContext.Current; // From control
ctx.Send\Post... // From worker thread

6voto

JWP Punkte 6290

Hier ist ein neuer Blick auf ein uraltes Thema in einem funktionelleren Stil. Wenn Sie die TaskXM-Klasse in all Ihren Projekten verwenden, müssen Sie sich mit nur einer Zeile Code nie wieder Gedanken über Thread-übergreifende Aktualisierungen machen.

public class Example
{
    /// <summary>
    /// No more delegates, background workers, etc. Just one line of code as shown below.
    /// Note it is dependent on the Task Extension method shown next.
    /// </summary>
    public async void Method1()
    {
        // Still on the GUI thread here if the method was called from the GUI thread
        // This code below calls the extension method which spins up a new task and calls back.
        await TaskXM.RunCodeAsync(() =>
        {
            // Running an asynchronous task here
            // Cannot update the GUI thread here, but can do lots of work
        });
        // Can update GUI on this line
    }
}

/// <summary>
/// A class containing extension methods for the Task class
/// </summary>
public static class TaskXM
{
    /// <summary>
    /// RunCodeAsyc 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 RunCodeAsync(Action Code)
    {
        await Task.Run(() =>
        {
            Code();
        });
        return;
    }
}

0 Stimmen

Die eigentliche Frage lautet: Wie funktioniert das Einwickeln Task.Run unterscheiden sich von einem einfachen Aufruf await Task.Run(() => {...}); ? Was ist der Vorteil dieser Umleitung?

0 Stimmen

Oberflächlich betrachtet kein Unterschied. Bei näherer Betrachtung zeigt sich jedoch die Stärke der funktionalen Programmierung. Insbesondere wickelt es eine statische Methode ein, was bei der Einzelverantwortung hilft. Was wäre, wenn Sie nun ConfigureAwait(false) implementieren oder eine Protokollierungsanweisung hinzufügen wollten. Das können Sie jetzt nur einmal tun.

6voto

Vielleicht ein bisschen überdosiert, aber das ist die Art und Weise, wie ich das normalerweise löse:

Wegen der Synchronisierung sind hier keine Aufrufe erforderlich. Das BasicClassThreadExample ist nur eine Art Layout für mich, so ändern Sie es, um Ihre tatsächlichen Bedürfnisse passen.

Es ist einfach, weil Sie nicht brauchen, um das Zeug in der UI-Thread zu behandeln!

public partial class Form1 : Form
{
    BasicClassThreadExample _example;

    public Form1()
    {
        InitializeComponent();
        _example = new BasicClassThreadExample();
        _example.MessageReceivedEvent += _example_MessageReceivedEvent;
    }

    void _example_MessageReceivedEvent(string command)
    {
        listBox1.Items.Add(command);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        listBox1.Items.Clear();
        _example.Start();
    }
}

public class BasicClassThreadExample : IDisposable
{
    public delegate void MessageReceivedHandler(string msg);

    public event MessageReceivedHandler MessageReceivedEvent;

    protected virtual void OnMessageReceivedEvent(string msg)
    {
        MessageReceivedHandler handler = MessageReceivedEvent;
        if (handler != null)
        {
            handler(msg);
        }
    }

    private System.Threading.SynchronizationContext _SynchronizationContext;
    private System.Threading.Thread _doWorkThread;
    private bool disposed = false;

    public BasicClassThreadExample()
    {
        _SynchronizationContext = System.ComponentModel.AsyncOperationManager.SynchronizationContext;
    }

    public void Start()
    {
        _doWorkThread = _doWorkThread ?? new System.Threading.Thread(dowork);

        if (!(_doWorkThread.IsAlive))
        {
            _doWorkThread = new System.Threading.Thread(dowork);
            _doWorkThread.IsBackground = true;
            _doWorkThread.Start();
        }
    }

    public void dowork()
    {
        string[] retval = System.IO.Directory.GetFiles(@"C:\Windows\System32", "*.*", System.IO.SearchOption.TopDirectoryOnly);
        foreach (var item in retval)
        {
            System.Threading.Thread.Sleep(25);
            _SynchronizationContext.Post(new System.Threading.SendOrPostCallback(delegate(object obj)
            {
                OnMessageReceivedEvent(item);
            }), null);
        }
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                _doWorkThread.Abort();
            }
            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~BasicClassThreadExample() { Dispose(false); }

}

6voto

Alexander Egorov Punkte 592

Ein weiteres Beispiel zu diesem Thema: Ich habe eine abstrakte Klasse, UiSynchronizeModel, erstellt, die eine gemeinsame Methodenimplementierung enthält:

public abstract class UiSynchronizeModel
{
    private readonly TaskScheduler uiSyncContext;
    private readonly SynchronizationContext winformsOrDefaultContext;

    protected UiSynchronizeModel()
    {
        this.winformsOrDefaultContext = SynchronizationContext.Current ?? new SynchronizationContext();
        this.uiSyncContext = TaskScheduler.FromCurrentSynchronizationContext();
    }

    protected void RunOnGuiThread(Action action)
    {
        this.winformsOrDefaultContext.Post(o => action(), null);
    }

    protected void CompleteTask(Task task, TaskContinuationOptions options, Action<Task> action)
    {
        task.ContinueWith(delegate
        {
            action(task);
            task.Dispose();
        }, CancellationToken.None, options, this.uiSyncContext);
    }
}

Ihre Modell- oder Controller-Klasse sollte von dieser abstrakten Klasse abgeleitet werden. Sie können jedes beliebige Muster verwenden (Tasks oder manuell verwaltete Hintergrund-Threads) und diese Methoden wie folgt nutzen:

public void MethodThatCalledFromBackroundThread()
{
   this.RunOnGuiThread(() => {
       // Do something over UI controls
   });
}

Beispiel für Aufgaben:

var task = Task.Factory.StartNew(delegate
{
    // Background code
    this.RunOnGuiThread(() => {
        // Do something over UI controls
    });
});

this.CompleteTask(task, TaskContinuationOptions.OnlyOnRanToCompletion, delegate
{
    // Code that can safely use UI controls
});

6voto

Jos Bosmans Punkte 161

Ich wollte eine Warnung hinzufügen, weil mir aufgefallen ist, dass in einigen der einfachen Lösungen die InvokeRequired prüfen.

Mir ist aufgefallen, dass, wenn Ihr Code ausgeführt wird bevor das Fensterhandle des Steuerelements erstellt wurde (z. B. bevor das Formular angezeigt wird), Invoke eine Ausnahme auslöst. Ich empfehle daher, immer zu prüfen, ob InvokeRequired vor dem Aufruf Invoke o BeginInvoke .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X