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 false
olarak 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:
- Denetimi System.Windows.Forms.Control.Invoke çağırmak için ana iş parçacığından bir temsilci çağıran yöntemi.
- 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 SafeCallDelegate
Text özelliğinin ayarlanmasını TextBox sağlar. WriteTextSafe
yöntemi sorgularInvokeRequired. döndürürsetrue
InvokeRequired, WriteTextSafe
gerçek çağrıyı Invoke denetime yapmak için yöntemine geçirirSafeCallDelegate
. döndürürse InvokeRequired false
, 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 RunWorkerCompleted Text ö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.
.NET Desktop feedback