public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//на форме расположен TextBox и Button.
//В TextBox будет выводиться количество сработок таймера,
//таймер запускается по нажатию на кнопку Button
timer = new Timer(AsyncHandler);
}
private void button1_Click(object sender, EventArgs e)
{
timer.Change(1000, 1000);
}
private void AsyncHandler(object data)
{
tickCount++;
textBox1.Text = tickCount.ToString();
}
private readonly System.Threading.Timer timer;
private int tickCount;
}
private void AsyncHandler(object data)
{
tickCount++;
Action<int> action = DoChangeTicks;
if (InvokeRequired)
{
Invoke(action, tickCount);
}
else
{
action(tickCount);
}
}
private void DoChangeTicks(int count)
{
textBox1.Text = tickCount.ToString();
}
private void AsyncHandler(object data)
{
tickCount++;
Action action = () => textBox1.Text = tickCount.ToString();
if (InvokeRequired)
{
Invoke(action);
}
else
{
action();
}
}
/// <summary>
/// Расширения облегчающие работу с элементами управления в многопоточной среде.
/// </summary>
public static class ControlExtentions
{
/// <summary>
/// Вызов делегата через control.Invoke, если это необходимо.
/// </summary>
/// <param name="control">Элемент управления</param>
/// <param name="doit">Делегат с некоторым действием</param>
public static void InvokeIfNeeded(this Control control, Action doit)
{
if (control.InvokeRequired)
control.Invoke(doit);
else
doit();
}
/// <summary>
/// Вызов делегата через control.Invoke, если это необходимо.
/// </summary>
/// <typeparam name="T">Тип параметра делегата</typeparam>
/// <param name="control">Элемент управления</param>
/// <param name="doit">Делегат с некоторым действием</param>
/// <param name="arg">Аргумент делагата с действием</param>
public static void InvokeIfNeeded<T>(this Control control, Action<T> doit, T arg)
{
if (control.InvokeRequired)
control.Invoke(doit, arg);
else
doit(arg);
}
}
private void AsyncHandler(object data)
{
tickCount++;
this.InvokeIfNeeded(
() => textBox1.Text = tickCount.ToString()
);
}
Прикольно! Блин, как много нужно прочитать...
ОтветитьУдалитьХорошей альтернативой которую можна использовать не только в Windows Forms а всюда где надо маршалить вызовы между потоками будет SynchronizationContext .
ОтветитьУдалитьКонечно, согласен. Но, как и во многих других вопросах, специализированные решения оказываются более простые в рамках одной задачи.
ОтветитьУдалитьЗдесь тоже самое. Способ, описанный выше - один из самых удобных способов работы с многопоточностью в Windows Forms, но при этом он не универсален.
Хотя знание общих подходов и принципов также весьма полезно.
Боюсь, что для того чтобы использовать SynchronizationContext в других сферах кроме WinForms и WPF придется изрядно попотеть, т.к. сам по себе класс SynchronizationContext не предоставляет нужную функциональность для маршалинга вызовов в определенный поток. В случае Send() он использует ThreadPool.QueueUserWorkItem, а в случае Post() вызывает метод в том же потоке. Поэтому если хочется иметь возможность делать маршалинг вызова в определенный поток, то придется этот механизм реализовывать вручную. В .net есть только 2 готовых реализации - для WinForms (WindowsFormsSynchronizationContext) и для WPF (DispatcherSynchronizationContext). Кстати, WindowsFormsSynchronizationContext реализован именно через механизм Control.BeginInvoke.
ОтветитьУдалитьКак вариант, мне больше нравится:
ОтветитьУдалитьthis.Invoke(new EventHandler(delegate
{
textBox1.Text = tickCount.ToString()
}));
Подсмотрено на http://www.codeproject.com/KB/threads/ThreadingDotNet2.aspx
Из плюсов нет необходимости в дополнительных классах а логика программы так-же прозрачна
Ну, твой код ничем не отличается от приведенного:
ОтветитьУдалитьAction action = () => textBox1.Text = tickCount.ToString();
if (InvokeRequired)
{
Invoke(action);
}
else
{
action();
}
Только используется синтаксис анонимных делегатов из C# 2.0, а этот вариант использует синтаксис C# 3.0.
Причем, если рассмотреть приведенный мною вариант без проверки на InvokeRequired, то код будет таким:
Action action = () => textBox1.Text = tickCount.ToString();
textBox.Invoke(action);
Что, как по мне, короче, понятнее и красивее:)
2Денис: чем тебе не нравится новый синтаксис анонимных делегатов в C#3.0?
"чем тебе не нравится новый синтаксис анонимных делегатов в C#3.0?"
ОтветитьУдалитьТем, что это синтаксис 3.0 ! FrameWork 3.0 не поддерживается на Windows 2000, поэтому приходится писать на FrameWork 2.0
2Drakosha: а откуда Вы знаете под какую винду Денис пишет код?
ОтветитьУдалитьЯ же не говорил абстрактно, я задавал вопрос конкретному человеку, который, насколько я помню пишет именно под .net 3.5 :)
У меня вопрос. Уже во втором посте я вижу, что вы пытаетесь заменить отдельные функции лямбдами. Я же читал (честно - не помню где), что более "православно" разделять функционал на как можно более мелкие функции и стремиться к тому, чтобы код фукции влазил на один экран. Лично мне тоже проще понять, что происходит в фукции, если она маленькая.
ОтветитьУдалитьУчитывая это, могли бы вы объяснить свой подход?
Спасибо.
Так как Control.InvokeRequired возвращает false не только при вызове из контекста GUI, но и если у контрола не создан дескриптор, не лучше ли писать как-то так:
ОтветитьУдалитьif(control.InvokeRequired)
{
control.Invoke(doit);
}
else if(!control.IsHandleCreated)
{
throw new InvalidOperationException();
}
else
{
doit();
}
Или я не прав?