C # Multi Threading - перемещать объекты между потоками - PullRequest
9 голосов
/ 02 июня 2010

Я работаю с элементом управления winforms, который является одновременно элементом GUI, а также выполняет некоторую внутреннюю обработку, которая не была предоставлена ​​разработчику. Когда создается экземпляр этого компонента, может потребоваться от 5 до 15 секунд, чтобы подготовиться, поэтому я хочу поместить его в другой поток, а после завершения вернуть его обратно в поток графического интерфейса и поместить в мою форму. Проблема заключается в том, что это вызовет (и имеет) исключение кросс-потока.

Обычно, когда я работаю с рабочими потоками, это просто с простыми объектами данных, я могу отодвинуться после завершения обработки и затем использовать элементы управления, уже находящиеся в главном потоке, но мне никогда не нужно было перемещать весь элемент управления таким образом.

Кто-нибудь знает, возможно ли это, и если да, то как? Если нет, то как можно решить проблему, подобную этой, где есть возможность заблокировать основной графический интерфейс?

Ответы [ 4 ]

3 голосов
/ 02 июня 2010

Вам не нужно блокировать графический интерфейс, вам просто нужно вызвать invoke:

Элементы управления в Windows Forms связаны с конкретная нить и не нить безопасный. Поэтому, если вы звоните Метод управления из другого нить, вы должны использовать один из методы вызова элемента управления для маршала вызов в нужную ветку. это свойство может быть использовано для определения Вы должны вызвать метод вызова, который может быть полезным, если вы не знаете, что поток владеет контролем. исх

Вот как это выглядит в коде:

public delegate void ComponentReadyDelegate(YourComponent component);
public void LoadComponent(YourComponent component)
{
    if (this.InvokeRequired)
    {
        ComponentReadyDelegate e = new ComponentReadyDelegate(LoadComponent);
        this.BeginInvoke(e, new object[]{component});
    }
    else
    {
        // The component is used by a UI control
        component.DoSomething();
        component.GetSomething();
    }
}

// From the other thread just initialize the component
// and call the LoadComponent method on the GUI.
component.Initialize(); // 5-15 seconds
yourForm.LoadComponent(component);

Обычно вызов LoadComponent из другого потока вызовет исключение между потоками, но в описанной выше реализации метод будет вызываться в потоке GUI.

InvokeRequired сообщает вам, если:

вызывающая сторона должна вызвать метод invoke при вызове метода к контролировать, потому что вызывающий абонент находится на другой поток, чем тот, который контроль был создан на. исх

Обновление:
Поэтому, если я правильно вас понимаю, объект управления создается в потоке, отличном от потока GUI, поэтому даже если вы смогли передать его в поток GUI, вы все равно не сможете использовать его, не вызвав межпотоковое исключение , Решением было бы создать объект в потоке GUI, но инициализировать его в отдельном потоке:

public partial class MyForm : Form
{
    public delegate void ComponentReadyDelegate(YourComponent component);
    private YourComponent  _component;
    public MyForm()
    {
        InitializeComponent();
        // The componet is created on the same thread as the GUI
        _component = new YourComponent();

        ThreadPool.QueueUserWorkItem(o =>
        {
            // The initialization takes 5-10 seconds
            // so just initialize the component in separate thread
            _component.Initialize();

            LoadComponent(_component);
        });
    }

    public void LoadComponent(YourComponent component)
    {
        if (this.InvokeRequired)
        {
            ComponentReadyDelegate e = new ComponentReadyDelegate(LoadComponent);
            this.BeginInvoke(e, new object[]{component});
        }
        else
        {
            // The component is used by a UI control
            component.DoSomething();
            component.GetSomething();
        }
    }
}
1 голос
/ 02 июня 2010

Не зная слишком много об объекте. Чтобы избежать исключений между потоками, вы можете сделать так, чтобы начальный поток вызывал вызов (даже если вы звоните из потока).

Скопировано и вставлено из одного из моих собственных приложений:

 private delegate void UpdateStatusBoxDel(string status);

    private void UpdateStatusBox(string status)
    {
        listBoxStats.Items.Add(status);
        listBoxStats.SelectedIndex = listBoxStats.Items.Count - 1;
        labelSuccessful.Text = SuccessfulSubmits.ToString();
        labelFailed.Text = FailedSubmits.ToString();
    }

    private void UpdateStatusBoxAsync(string status)
    {
        if(!areWeStopping)
            this.BeginInvoke(new UpdateStatusBoxDel(UpdateStatusBox), status);
    }

Таким образом, по сути, многопоточное задание будет вызывать метод "Async". Который потом скажет основную форму для начала вызова (собственно сам асинхронный).

Я полагаю, что, возможно, есть более короткий способ сделать все это без необходимости создания делегатов и двух разных методов. Но этот путь просто укоренился во мне. И это то, чему учат в книгах Microsoft: p

0 голосов
/ 03 июня 2010

Элемент управления должен быть создан и изменен в потоке пользовательского интерфейса, этого нет.

Чтобы пользовательский интерфейс реагировал при длительной инициализации, поддерживайте процесс в фоновом потоке и вызывайте любой элемент управления. Пользовательский интерфейс должен оставаться отзывчивым, но если это не так, вы можете добавить некоторое время ожидания в фоновый поток. Это пример использования параллельных инструментов .Net 4: http://www.lovethedot.net/2009/01/parallel-programming-in-net-40-and_30.html

Если взаимодействие с конкретным инициализируемым элементом управления не может быть разрешено до завершения инициализации, то скрыть или отключить его до завершения.

0 голосов
/ 02 июня 2010

Класс BackgroundWorker предназначен именно для этой ситуации. Он будет управлять потоком для вас, и позволит вам запустить поток, а также отменить поток. Поток может отправлять события обратно в поток GUI для обновления статуса или завершения. Обработчики событий для этих событий состояния и завершения находятся в главном потоке графического интерфейса и могут обновлять элементы управления WinForm. И WinForm не блокируется. Это все, что вам нужно. (И одинаково хорошо работает в WPF и Silverlight.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...