вторник, 17 марта 2009 г.

Многопоточность в Windows Forms. Control.Invoke().

Человек даже немного поработав с Windows Forms наверняка сталкивался с замечательным исключением System.InvalidOperationException с таким описанием: {"Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on."} Этим сообщением среда выполнения недвусмысленно дает понять, что обращаться к элементам управления пользовательского интерфейса можно только из того потока, который создал этот элемент управления. Такое поведение обусловлено деталями реализации, в частности использованием однопоточной модели аппартаментов (Single-threaded Apartment, STA) и механизма обработки оконных сообщений. Вот простенький пример кода, который приводит к генерации такого исключения:

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;

}

Существует вполне простой и понятный способ решения этой проблемы, путем проверки свойства элемента управления InvokeRequired с последующим вызовом метода Invoke.
Вот самый простой и примитивный способ:

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();

}

Способ простой, но не совсем удобный. В данном случае вспомогательный метод только увеличивает связность и усложняет понимает решаемой задачи. Если воспользоваться новшествами C#3.0 в виде лямбда-выражений, можно сделать несколько более удобную реализацию следующего вида:

private void AsyncHandler(object data)

{

    tickCount++;

    Action action = () => textBox1.Text = tickCount.ToString();

    if (InvokeRequired)

    {

        Invoke(action);

    }

    else

    {

        action();

    }

}

Уже лучше. Мы избавились от необязательного метода, который неразрывно связан с тем действием, которое выполняет метод AsyncHandler. Но, все же, еще есть над чем подумать.
Следующий вариант решения был честно подсмотрен в неплохой книжке: Bill Wagner "More Effective C#".

/// <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()

            );

}

вторник, 3 марта 2009 г.

Полезные ссылки от Джона Роббинса

Вот решил собрать полезные ссылки, инструменты и статьи, которые советует Джон Роббинс в своей книге "Отладка приложений для Microsoft .Net".
СТАТЬИ
Оптимизация загрузки приложений Windows Forms
1. Practical Tips For Boosting The Performance Of Windows Forms Apps http://msdn.microsoft.com/en-us/magazine/cc163630.aspx
2. CLR Inside Out. Improving Application Startup Time http://msdn.microsoft.com/en-us/magazine/cc163655.aspx
Пользовательский интерфейс и юзабилити
1. Windows Vista User Experience Guidelines. http://msdn.microsoft.com/en-us/library/aa511258.aspx
2. The Joel Test: 12 Steps to Better Code. http://www.joelonsoftware.com/articles/fog0000000043.html
О финализаторах
1. Keep Your Code Running with the Reliability Features of the .NET Framework. http://msdn.microsoft.com/en-us/magazine/cc163716.aspx
О вреде разработки ПО под учетной записью с правами администратора:
2. MakeMeAdmin -- temporary admin for your Limited User account http://blogs.msdn.com/aaron_margosis/archive/2004/07/24/193721.aspx
Разное
1. SUPERASSERT.NET SUPERASSERT Goes .NET http://msdn.microsoft.com/en-us/magazine/cc188701.aspx
2. Автоматизация тестирования пользовательского интерфейса. Build Quick and Easy UI Test Automation Suites with Visual Studio .NET. http://msdn.microsoft.com/en-us/magazine/cc188784.aspx
4. SOS metadata commands. Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects. http://msdn.microsoft.com/ru-ru/magazine/cc163791(en-us).aspx
5. How do I debug assembles in the Global Assembly Cache (GAC)? http://blogs.msdn.com/junfeng/archive/2005/12/13/503059.aspx
6. Document Your Code in No Time At All with Macros in Visual Studio. http://msdn.microsoft.com/ru-ru/magazine/cc163757(en-us).aspx
8. Speeding up the C# Source Code Editor http://weblogs.asp.net/rweigelt/archive/2006/05/15/446536.aspx
9. Let The CLR Find Bugs For You With Managed Debugging Assistants. http://msdn.microsoft.com/ru-ru/magazine/cc163606(en-us).aspx

БЛОГИ

1. Блог Майкла Столла CLR Debugging Team: http://blogs.gotdotnet.com/jmstall/
2. Блог Марка Руссиновича: http://blogs.technet.com/markrussinovich/
3. Блог Джо Даффи: http://www.bluebytesoftware.com/blog/
4. Блог Tess Ferrandez: http://blogs.msdn.com/tess/
5. Блог Jeff Atwood: http://www.codinghorror.com/blog/
6. Блог Aaron Margosis "Non-Admin" WebLog: http://blogs.msdn.com/aaron_margosis/
7. Блоги Wintellectuals' Blogs: http://www.wintellect.com/cs/blogs/
ИНСТРУМЕНТЫ, УТИЛИТЫ
1. Reflector Лутца Родера
1.1. Сам рефлектор: http://www.aisto.com/roeder/dotnet/
1.2. Denis Bauer Reflector.FileDisassembler. Позволяет дизасемблировать целиком сборку и сохранить ее исходный код. http://www.denisbauer.com/NETTools/FileDisassembler.aspx
1.3. Reflector Diff AddIn. http://www.codingsanity.com/diff.htm
1.4. Kevin Dente's Running Assembly Reflector Add-in. http://Weblogs.asp.net/kdente/articles/438539.aspx На этом перечень AddIn-ов к рефлектору не заканчивается и в интернете можно свободно найти достаточное количество полезных дополнений.
2. Иструменты от Sysinternals. Process Explorer, Process Monitor, DebugView и многое другое. http://technet.microsoft.com/en-us/sysinternals/default.aspx
3. Автономная утилита анализа кода FxCop msdn.microsoft.com/en-us/library/bb429476(VS.80).aspx
4. Debugging Tools for Windows. WinDBG, SOS, ADPlus. www.microsoft.com/whdc/devtools/debugging/ Очень полезные инструменты для отладки, исследования памяти, поиска deadlock-ов и многих других задач.
6. NCover - A test code coverage tool for C# .NET http://ncover.sourceforge.net/
7. Dependency Walker from http://www.dependencywalker.com/
8. Набор инструментов от Sells Brothers. RegexDesigner.NET и другие. http://www.sellsbrothers.com/tools/
9. CopySourceAsHtml (CSAH). Встраиваемый модуль в Visual Studio, который позволяет копировать куски кода виде html. www.jtleigh.com/CopySourceAsHtml/
10. Windows Installer XML (WIX). http://wix.sourceforge.net/ Должно быть интересно посмотреть на бесплатный инструмент от Майкрософт, с помощью которого создаются инсталляционные пакеты для Microsoft Office, Visual Studio и SQL Server. wix.sourceforge.net.
1. GhostDoc. Free add-in for Visual Studio that automatically generates XML documentation comments for C#. http://www.roland-weigelt.de/ghostdoc/
14. Инструменты автоматизации тестирования пользовательского интерфейса: Mercury WinRunner, Rational Robot
15. PInvoke.Net. Do Interop the WIKI way. http://www.pinvoke.net/
17. Regex Kit: Regular Expression Visualizers for VS 2005 http://weblogs.asp.net/rosherove/archive/2005/11/26/AnnoucingRegexKit10.aspx