В программе создаются и удаляются объекты, в них есть события и метод который запускается в отдельном потоке. В этом методе который будет работать в отдельном потоке будет вызвано это событие. В форме нужно по этому событию обновить контрол. Вот код в упрощенном виде:
class class1
{
//событие на которое вешается SomeEventHandler
public event EventHandler event1;
public class1()
{
new Thread(Method1).Start();
}
private void Method1()
{
//выполняется в фоновом потоке
event1?.Invoke();
}
}
class Form1
{
SomeEventHandler()
{
//взаимодействие с формой
//сюда нужно добавить что то, что будет взаимодействовать с формой но из основного потока
}
}
Как видно SomeEventHandler будет вызван в другом потоке, но тогда в этом обработчике нельзя обратится к контролу формы. Как это обойти?
Конструктор class1 тоже запускается не в основном потоке, не из интерфейса.
Экземпляр класса Worker
в данном примере должен создаваться в UI потоке, потому что в его конструкторе мы получаем контекст синхронизации .
Если не хочется в класс, содержащий код, который работает в другом потоке пихать контекст синхронизации, то просто внутри метода обработчика в классе окна (SomeEventHandler
)вызывай метод Invoke (кстати, там ответ написал Джон Скит - автор книг по C# и .NET технологиям)
В WPF
и Windows Forms
разные реализации SynchronizationContext
. То есть в зависимости от технологии он по разному перенаправляет выполнение в UI
поток (если речь о UI
).
Заметь, что без
_sycnContext = SynchronizationContext.Current ?? new SynchronizationContext();
В консольном приложении не заработает, так как SynchronizationContext.Current
там будет null
.
Worker.cs
using System;
using System.Threading;
namespace WorkerLib
{
public class Worker
{
private readonly SynchronizationContext _syncContext;
public Worker()
{
_syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
}
public event EventHandler Started;
public event EventHandler Completed;
private void StartOperation(object state)
{
_syncContext.Post(OnStarted, null);
// Делаем работу
Thread.Sleep(1250);
_syncContext.Post(OnCompleted, null);
}
public void DoWork()
{
ThreadPool.QueueUserWorkItem(StartOperation);
}
private void OnCompleted(object state)
{
var handler = Completed;
if (handler != null)
handler(this, EventArgs.Empty);
}
private void OnStarted(object state)
{
var handler = Started;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
}
Windows Forms
MainForm.cs
using System;
using System.Windows.Forms;
using WorkerLib;
namespace WinForms_SyncContextExample
{
public partial class MainForm : Form
{
private readonly Worker _worker;
public MainForm()
{
InitializeComponent();
_worker = new Worker();
_worker.Started += (sender, args) => infoLabel.Text = "Запустили работу";
_worker.Completed += (sender, args) => infoLabel.Text = "Работа завершена";
}
private void WorkButton_Click(object sender, EventArgs e)
{
_worker.DoWork();
}
}
}
Console
Program.cs
using System;
using WorkerLib;
namespace SyncContextExample
{
internal class Program
{
private static void Main(string[] args)
{
Worker worker = new Worker();
worker.Started += (sender, args) => Console.WriteLine("Запустили работу");
worker.Completed += (sender, args) => Console.WriteLine("Работа завершена");
worker.DoWork();
Console.ReadKey();
}
}
}
Создайте синглтон, общий для всех потоков, и положите в него экземпляр вашей формы. Тогда другой поток сможет взаимодействовать с ней по ссылке.
Однако будьте осторожны: может произойти такое, что с одним полем взаимодействуют сразу оба потока, т.е. решение непотокобезопасно.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
В Visual Studio есть Windows Forms ApplicationА есть ли это в Rider? Никак не могу найти
Доброго времени сутокПытаюсь реализовать очередь на отправку сообщений