Share via


Nasıl yapılır: Windows Forms denetimlerine iş parçacığı açısından güvenli çağrılar yapma

Çoklu iş parçacığı kullanımı, Windows Forms uygulamalarının performansını artırabilir, ancak Windows Forms denetimlerine erişim doğal olarak iş parçacığı açısından güvenli değildir. Çoklu iş parçacığı kullanımı kodunuzu çok ciddi ve karmaşık hatalara maruz bırakabilir. Denetimi yönlendiren iki veya daha fazla iş parçacığı, denetimi tutarsız bir duruma zorlayabilir ve yarış koşullarına, kilitlenmelere ve donmalara veya kilitlenmelere yol açabilir. Uygulamanızda çok iş parçacığı kullanımı uygularsanız, iş parçacığı arası denetimleri iş parçacığı güvenli bir şekilde çağırdığınızdan emin olun. Daha fazla bilgi için bkz . Yönetilen iş parçacığı oluşturma en iyi yöntemleri.

Bu denetimi oluşturmayan bir iş parçacığından Windows Forms denetimini güvenli bir şekilde çağırmanın iki yolu vardır. yöntemini kullanarak System.Windows.Forms.Control.Invoke ana iş parçacığında oluşturulan ve ardından denetimi çağıran bir temsilci çağırabilirsiniz. Alternatif olarak, arka plan iş parçacığında yapılan işleri sonuçlar üzerinde raporlamadan ayırmak için olay temelli bir model kullanan bir System.ComponentModel.BackgroundWorkeruygulayabilirsiniz.

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

Doğrudan oluşturmamış bir iş parçacığından bir denetimi çağırmak güvenli değildir. Aşağıdaki kod parçacığı, denetime yönelik güvenli olmayan bir çağrıyı System.Windows.Forms.TextBox gösterir. Olay işleyicisi Button1_Click , ana iş parçacığının özelliğini doğrudan ayarlayan yeni WriteTextUnsafe bir iş parçacığı TextBox.Text oluşturur.

private void Button1_Click(object sender, EventArgs e)
{
    thread2 = new Thread(new ThreadStart(WriteTextUnsafe));
    thread2.Start();
}
private void WriteTextUnsafe()
{
    textBox1.Text = "This text was set unsafely.";
}
Private Sub Button1_Click(ByVal sender As Object, e As EventArgs) Handles Button1.Click
    Thread2 = New Thread(New ThreadStart(AddressOf WriteTextUnsafe))
    Thread2.Start()
End Sub

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

Visual Studio hata ayıklayıcısı, iş parçacıkları arası işlem geçerli değil iletisiyle bir InvalidOperationException oluşturarak bu güvenli olmayan iş parçacığı çağrılarını algılar. Oluşturulduğu iş parçacığı dışında bir iş parçacığından erişilen "" denetimi. Her InvalidOperationException zaman Visual Studio hata ayıklaması sırasında güvenli olmayan iş parçacıkları arası çağrılar için gerçekleşir ve uygulama çalışma zamanında gerçekleşebilir. Sorunu düzeltmeniz gerekir, ancak özelliğini falseolarak ayarlayarak Control.CheckForIllegalCrossThreadCalls özel durumu devre dışı bırakabilirsiniz.

İş parçacıkları arası çağrıları Kasa

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

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

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

Bu örnekleri C# veya Visual Basic komut satırından .NET Framework uygulamaları olarak derleyebilir ve çalıştırabilirsiniz. Daha fazla bilgi için bkz. csc.exe ile komut satırı oluşturma veya Komut satırından derleme (Visual Basic).

.NET Core 3.0'dan başlayarak, .NET Core Windows Forms <klasör adı.csproj> proje dosyası olan bir klasörden örnekleri Windows .NET Core uygulamaları olarak derleyebilir ve çalıştırabilirsiniz.

Örnek: Invoke yöntemini bir temsilciyle kullanma

Aşağıdaki örnek, Windows Forms denetimine iş parçacığı açısından güvenli çağrılar sağlamaya yönelik bir deseni gösterir. Denetimin System.Windows.Forms.Control.InvokeRequired oluşturma iş parçacığı kimliğini çağıran iş parçacığı kimliğiyle karşılaştıran özelliğini sorgular. İş parçacığı kimlikleri aynıysa, denetimi doğrudan çağırır. İş parçacığı kimlikleri farklıysa, asıl çağrıyı Control.Invoke denetime yapan ana iş parçacığından bir temsilci ile yöntemini çağırır.

denetimin SafeCallDelegateText özelliğinin ayarlanmasını TextBox sağlar. WriteTextSafe yöntemi sorgularInvokeRequired. döndürürsetrueInvokeRequired, WriteTextSafe gerçek çağrıyı Invoke denetime yapmak için yöntemine geçirirSafeCallDelegate. döndürürse InvokeRequiredfalse, WriteTextSafe öğesini doğrudan ayarlar TextBox.Text . Olay işleyicisi Button1_Click yeni iş parçacığını oluşturur ve yöntemini çalıştırır WriteTextSafe .

using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

public class InvokeThreadSafeForm : Form
{
    private delegate void SafeCallDelegate(string text);
    private Button button1;
    private TextBox textBox1;
    private Thread thread2 = null;

    [STAThread]
    static void Main()
    {
        Application.SetCompatibleTextRenderingDefault(false);
        Application.EnableVisualStyles();
        Application.Run(new InvokeThreadSafeForm());
    }
    public InvokeThreadSafeForm()
    {
        button1 = new Button
        {
            Location = new Point(15, 55),
            Size = new Size(240, 20),
            Text = "Set text safely"
        };
        button1.Click += new EventHandler(Button1_Click);
        textBox1 = new TextBox
        {
            Location = new Point(15, 15),
            Size = new Size(240, 20)
        };
        Controls.Add(button1);
        Controls.Add(textBox1);
    }

    private void Button1_Click(object sender, EventArgs e)
    {
        thread2 = new Thread(new ThreadStart(SetText));
        thread2.Start();
        Thread.Sleep(1000);
    }

    private void WriteTextSafe(string text)
    {
        if (textBox1.InvokeRequired)
        {
            var d = new SafeCallDelegate(WriteTextSafe);
            textBox1.Invoke(d, new object[] { text });
        }
        else
        {
            textBox1.Text = text;
        }
    }

    private void SetText()
    {
        WriteTextSafe("This text was set safely.");
    }
}
Imports System.Drawing
Imports System.Threading
Imports System.Windows.Forms

Public Class InvokeThreadSafeForm : Inherits Form

    Public Shared Sub Main()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.EnableVisualStyles()
        Dim frm As New InvokeThreadSafeForm()
        Application.Run(frm)
    End Sub

    Dim WithEvents Button1 As Button
    Dim TextBox1 As TextBox
    Dim Thread2 as Thread = Nothing

    Delegate Sub SafeCallDelegate(text As String)

    Private Sub New()
        Button1 = New Button()
        With Button1
            .Location = New Point(15, 55)
            .Size = New Size(240, 20)
            .Text = "Set text safely"
        End With
        TextBox1 = New TextBox()
        With TextBox1
            .Location = New Point(15, 15)
            .Size = New Size(240, 20)
        End With
        Controls.Add(Button1)
        Controls.Add(TextBox1)
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Thread2 = New Thread(New ThreadStart(AddressOf SetText))
        Thread2.Start()
        Thread.Sleep(1000)
    End Sub

    Private Sub WriteTextSafe(text As String)
        If TextBox1.InvokeRequired Then
            Dim d As New SafeCallDelegate(AddressOf SetText)
            TextBox1.Invoke(d, New Object() {text})
        Else
            TextBox1.Text = text
        End If
    End Sub

    Private Sub SetText()
        WriteTextSafe("This text was set safely.")
    End Sub
End Class

Örnek: BackgroundWorker olay işleyicisi kullanma

Çok iş parçacıklı işlem gerçekleştirmenin System.ComponentModel.BackgroundWorker kolay bir yolu, olay temelli bir model kullanan bileşendir. Arka plan iş parçacığı olayı çalıştırır BackgroundWorker.DoWork ve ana iş parçacığıyla etkileşim kurmaz. Ana iş parçacığı, ana iş parçacığının BackgroundWorker.ProgressChanged denetimlerini çağırabilen ve BackgroundWorker.RunWorkerCompleted olay işleyicilerini çalıştırır.

kullanarak BackgroundWorkeriş parçacığı güvenli bir çağrı yapmak için, arka plan iş parçacığında işi yapmak için bir yöntem oluşturun ve bunu DoWork olaya bağlayın. Arka plan çalışmasının sonuçlarını raporlamak ve veya RunWorkerCompleted olayına bağlamak için ana iş parçacığında ProgressChanged başka bir yöntem oluşturun. Arka plan iş parçacığını başlatmak için öğesini çağırın BackgroundWorker.RunWorkerAsync.

Örnek, denetimin RunWorkerCompletedText özelliğini ayarlamak için olay işleyicisini TextBox kullanır. Olayı kullanan ProgressChanged bir örnek için bkz BackgroundWorker. .

using System;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

public class BackgroundWorkerForm : Form
{
    private BackgroundWorker backgroundWorker1;
    private Button button1;
    private TextBox textBox1;

    [STAThread]
    static void Main()
    {
        Application.SetCompatibleTextRenderingDefault(false);
        Application.EnableVisualStyles();
        Application.Run(new BackgroundWorkerForm());
    }
    public BackgroundWorkerForm()
    {
        backgroundWorker1 = new BackgroundWorker();
        backgroundWorker1.DoWork += new DoWorkEventHandler(BackgroundWorker1_DoWork);
        backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorker1_RunWorkerCompleted);
        button1 = new Button
        {
            Location = new Point(15, 55),
            Size = new Size(240, 20),
            Text = "Set text safely with BackgroundWorker"
        };
        button1.Click += new EventHandler(Button1_Click);
        textBox1 = new TextBox
        {
            Location = new Point(15, 15),
            Size = new Size(240, 20)
        };
        Controls.Add(button1);
        Controls.Add(textBox1);
    }
    private void Button1_Click(object sender, EventArgs e)
    {
        backgroundWorker1.RunWorkerAsync();
    }

    private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        // Sleep 2 seconds to emulate getting data.
        Thread.Sleep(2000);
        e.Result = "This text was set safely by BackgroundWorker.";
    }

    private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        textBox1.Text = e.Result.ToString();
    }
}
Imports System.ComponentModel
Imports System.Drawing
Imports System.Threading
Imports System.Windows.Forms

Public Class BackgroundWorkerForm : Inherits Form

    Public Shared Sub Main()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.EnableVisualStyles()
        Dim frm As New BackgroundWorkerForm()
        Application.Run(frm)
    End Sub

    Dim WithEvents BackgroundWorker1 As BackgroundWorker
    Dim WithEvents Button1 As Button
    Dim TextBox1 As TextBox

    Private Sub New()
        BackgroundWorker1 = New BackgroundWorker()
        Button1 = New Button()
        With Button1
            .Text = "Set text safely with BackgroundWorker"
            .Location = New Point(15, 55)
            .Size = New Size(240, 20)
        End With
        TextBox1 = New TextBox()
        With TextBox1
            .Location = New Point(15, 15)
            .Size = New Size(240, 20)
        End With
        Controls.Add(Button1)
        Controls.Add(TextBox1)
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) _
     Handles BackgroundWorker1.DoWork
        ' Sleep 2 seconds to emulate getting data.
        Thread.Sleep(2000)
        e.Result = "This text was set safely by BackgroundWorker."
    End Sub

    Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) _
     Handles BackgroundWorker1.RunWorkerCompleted
        textBox1.Text = e.Result.ToString()
    End Sub
End Class

Ayrıca bkz.