Denetimlere iş parçacığı güvenli çağrılar yapma (Windows Forms .NET)

Çoklu iş parçacığı kullanma, Windows Forms uygulamalarının performansını geliştirebilir, ancak Windows Forms denetimlerine erişim doğal olarak iş parçacığı için güvenli değildir. Çoklu iş parçacığı kullanarak kodunuzu ciddi ve karmaşık hatalara maruz abilirsiniz. Bir denetimin iş parçacığını yönlendiren iki veya daha fazla iş parçacığı, denetimi tutarsız bir durumda zorlar ve yarış koşullarına, kilitlenmelere, donmalara veya askıda kalmalarına neden olabilir. Uygulamanıza çoklu iş parçacığı uygulamanıza, iş parçacığı güvenli bir şekilde iş parçacığı çapraz denetimler çağırsanız emin olun. Daha fazla bilgi için bkz. Yönetilen iş parçacığı en iyi yöntemleri.

Önemli

.NET 6 ve .NET 5 için Masaüstü Kılavuzu belgeleri (.NET Core 3.1 dahil) hazır aşamasındadır.

Bir Windows Forms denetimi, bu denetimi oluşturmadan bir iş parçacığından güvenli bir şekilde çağırmanın iki yolu vardır. Ana System.Windows.Forms.Control.Invoke iş parçacığında oluşturulan ve denetimi çağıran bir temsilciyi çağıran yöntemini kullanın. Veya arka plan iş parçacığında yapılan işi sonuçlar üzerinde raporlamadan ayırmak için olay System.ComponentModel.BackgroundWorker odaklı bir model kullanan bir uygulaması da gerçekleştirebilirsiniz.

Güvenli olmayan iş parçacığı arası çağrılar

Bir denetimi oluşturmadan doğrudan bir iş parçacığından çağırma güvenli değildir. Aşağıdaki kod parçacığı, denetime yapılan güvenli olmayan bir çağrıyı System.Windows.Forms.TextBox göstermektedir. Olay Button1_Click işleyicisi, ana WriteTextUnsafe iş parçacığının özelliğini doğrudan ayaran yeni bir TextBox.Text iş parçacığı oluşturur.

private void button1_Click(object sender, EventArgs e)
{
    var thread2 = new System.Threading.Thread(WriteTextUnsafe);
    thread2.Start();
}

private void WriteTextUnsafe() =>
    textBox1.Text = "This text was set unsafely.";
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim thread2 As New System.Threading.Thread(AddressOf WriteTextUnsafe)
    thread2.Start()
End Sub

Private Sub WriteTextUnsafe()
    TextBox1.Text = "This text was set unsafely."
End Sub

Hata Visual Studio, iş parçacığı arası işlem geçerli değil iletisiyle bir göndererek bu güvenli InvalidOperationExceptionInvalidOperationException , hata ayıklama sırasında her zaman güvenli olmayan iş parçacığı Visual Studio ve uygulama InvalidOperationException çalışma zamanında oluşabilir. Sorunu düzeltmelisiniz, ancak özelliğini olarak ayarerek özel durumu devre Control.CheckForIllegalCrossThreadCalls dışı false abilirsiniz.

Kasa iş parçacığı çağrıları

Aşağıdaki kod örnekleri, bir Windows Forms denetimi oluşturmadan bir iş parçacığından güvenli bir şekilde çağırmanın iki yolu gösterir:

  1. denetimine System.Windows.Forms.Control.Invoke çağrı yapmak için ana iş parçacığından bir temsilci çağıran yöntemi.
  2. Olay System.ComponentModel.BackgroundWorker odaklı model sunan bir bileşen.

Her iki örnekte de arka plan iş parçacığı, bu iş parçacığında yapılan çalışmaların benzetimini yapmak için bir saniye uykudadır.

Örnek: Invoke yöntemini kullanma

Aşağıdaki örnek, Windows Forms denetimine iş parçacığı güvenli çağrılar sağlama desenini gösterir. Denetimin oluşturma iş parçacığı kimliğini çağıran iş parçacığı kimliğiyle System.Windows.Forms.Control.InvokeRequired karşılaştıran özelliğini sorgular. Bunlar farklı ise yöntemini Control.Invoke çağırsanız gerekir.

, WriteTextSafe denetimin TextBox özelliğini yeni bir Text değere ayarlamaya olanak sağlar. yöntemi InvokeRequired sorgular. InvokeRequiredtrue döndürürse, yöntemini WriteTextSafe yöntemine temsilci olarak geçirmeyi tekrar tekrar Invoke çağrır. InvokeRequiredfalse döndürürse, doğrudan WriteTextSafeTextBox.Text ayarlar. Olay Button1_Click işleyicisi yeni iş parçacığını oluşturur ve yöntemini WriteTextSafe çalıştırır.

private void button1_Click(object sender, EventArgs e)
{
    var threadParameters = new System.Threading.ThreadStart(delegate { WriteTextSafe("This text was set safely."); });
    var thread2 = new System.Threading.Thread(threadParameters);
    thread2.Start();
}

public void WriteTextSafe(string text)
{
    if (textBox1.InvokeRequired)
    {
        // Call this same method but append THREAD2 to the text
        Action safeWrite = delegate { WriteTextSafe($"{text} (THREAD2)"); };
        textBox1.Invoke(safeWrite);
    }
    else
        textBox1.Text = text;
}
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    Dim threadParameters As New System.Threading.ThreadStart(Sub()
                                                                 WriteTextSafe("This text was set safely.")
                                                             End Sub)

    Dim thread2 As New System.Threading.Thread(threadParameters)
    thread2.Start()

End Sub

Private Sub WriteTextSafe(text As String)

    If (TextBox1.InvokeRequired) Then

        TextBox1.Invoke(Sub()
                            WriteTextSafe($"{text} (THREAD2)")
                        End Sub)

    Else
        TextBox1.Text = text
    End If

End Sub

Örnek: BackgroundWorker kullanma

Çoklu iş parçacığı kullanımı uygulamanın kolay bir yolu, olay System.ComponentModel.BackgroundWorker odaklı bir model kullanan bileşenidir. Arka plan iş BackgroundWorker.DoWork parçacığı, ana iş parçacığıyla etkileşim kurmadan olayı yükselter. Ana iş parçacığı, BackgroundWorker.ProgressChanged ana BackgroundWorker.RunWorkerCompleted iş parçacığı denetimlerini çağıran ve olay işleyicilerini çalıştırır.

kullanarak iş parçacığı güvenli bir çağrı yapmak için BackgroundWorker olayı DoWork işle. Arka plan çalışanının durumu rapor etmek için kullandığı iki olay vardır: ProgressChanged ve RunWorkerCompleted . Olay, durum güncelleştirmelerini ana iş parçacığına iletir ve olay arka plan çalışanının çalışmalarını ProgressChangedRunWorkerCompleted tamamlamış olduğunu işaret etmek için kullanılır. Arka plan iş parçacığını başlatmak için çağrısında BackgroundWorker.RunWorkerAsync bulundu.

Örnek, olayda 0 ile 10 DoWork arasında sayar ve sayımlar arasında bir saniye duraklatma sağlar. Olay ProgressChanged işleyicisini, s numarayı ana iş parçacığına geri rapor etmek ve TextBox denetimin özelliğini ayarlamak için Text kullanır. Olayın ProgressChanged çalışması için BackgroundWorker.WorkerReportsProgress özelliğinin olarak ayarlanmış olması true gerekir.

private void button1_Click(object sender, EventArgs e)
{
    if (!backgroundWorker1.IsBusy)
        backgroundWorker1.RunWorkerAsync();
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    int counter = 0;
    int max = 10;

    while (counter <= max)
    {
        backgroundWorker1.ReportProgress(0, counter.ToString());
        System.Threading.Thread.Sleep(1000);
        counter++;
    }
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) =>
    textBox1.Text = (string)e.UserState;
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    If (Not BackgroundWorker1.IsBusy) Then
        BackgroundWorker1.RunWorkerAsync()
    End If

End Sub

Private Sub BackgroundWorker1_DoWork(sender As Object, e As ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

    Dim counter = 0
    Dim max = 10

    While counter <= max

        BackgroundWorker1.ReportProgress(0, counter.ToString())
        System.Threading.Thread.Sleep(1000)

        counter += 1

    End While

End Sub

Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
    TextBox1.Text = e.UserState
End Sub