2015-09-22 3 views
2

Я пишу код, который вызывается в потоке пользовательского интерфейса, вызывает некоторый код в другом потоке (а не ThreadPool, но тот же поток каждый раз), а затем возобновляется в потоке пользовательского интерфейса. Я бы хотел получить совет по лучшему асинхронному способу сделать это.Продолжая код в другом потоке

Сложность метода EnsureThread заключается в том, что другой поток должен быть одним и тем же потоком каждый раз и должен быть STA с запущенным на нем диспетчером. Это связано с тем, что мне нужно использовать MCI, но не хочу, чтобы он работал в потоке пользовательского интерфейса. См. Здесь https://stackoverflow.com/a/32711239/420159.

Я создаю второй поток, как так:

private static void EnsureThread() 
{ 
    if (eventLoopThread != null) 
    { 
     return; 
    } 

    lock (eventLoopLock) 
    { 
     if (eventLoopThread == null) 
     { 
      var lck = new EventWaitHandle(false, EventResetMode.ManualReset); 

      var t = new Thread(() => 
      { 
       try 
       { 
        // create dispatcher and sync context 
        var d = Dispatcher.CurrentDispatcher; 
        var context = new DispatcherSynchronizationContext(d); 
        SynchronizationContext.SetSynchronizationContext(context); 

        // create taskfactory 
        eventLoopFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()); 
        eventLoopDispatcher = d; 
       } 
       finally 
       { 
        lck.Set(); 
       } 

       // run the event loop 
       Dispatcher.Run(); 
      }); 
      t.SetApartmentState(ApartmentState.STA); 
      t.IsBackground = true; 
      t.Start(); 

      lck.WaitOne(); 
      lck.Dispose(); 

      eventLoopThread = t; 
     } 
    } 
} 

, а затем я называю второй поток следующим образом:

async void button_click(...) 
{ 
    // do something 1 

    await eventLoopFactory.StartNew(()=> 
    { 
     // do something 2 
    }); 

    // do something 3 
} 

Есть ли лучший способ сделать это?

+1

Этот метод 'EnsureThread' выглядит очень неудобным для меня. Чего вы пытаетесь достичь? Пока неясно. Пожалуйста, обновите вопрос. – Gusdor

+0

Готово. Короче говоря, я использую mciSendString, для которого требуется поток с циклом событий, но не хочу, чтобы он работал в потоке пользовательского интерфейса, потому что он замедляется из-за пользовательского интерфейса. –

ответ

4

Вам не нужно использовать нить завод, если вы хотите запустить делегата на резьбе ThreadPool

просто использовать

 await Task.Run(() => 
     { 
      // do something 2 
     }); 

Таким образом, вам не нужно запустите код // do something в потоке цикла событий, но он будет запущен в доступном потоке из пула потоков.

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

+0

Привет, целевой поток должен ** не ** быть ThreadPool. Это должен быть тот же самый поток каждый раз; следовательно, TaskScheduler. Я использую MCI, и он должен запускаться в потоке STA с циклом событий. Обычно вы используете поток пользовательского интерфейса, но он замедляет пользовательский интерфейс и хочет его отключить. –

+0

Не могли бы вы объяснить, почему на самом деле всегда нужно запускать ту же тему? –

+0

Я чувствую, что сделал это. См. Документацию по MCI (https://msdn.microsoft.com/en-us/library/dd757161(v=vs.85).aspx). Обратите внимание на параметр hwnd, подразумевающий требование для цикла события. Этот метод просто не будет работать на произвольном потоке, который является MTA или не работает Dispatcher. –

1

Ваш Button_Click уже, кажется, делает слишком много вещей, вы должны сначала разделить этот метод на отдельные методы.

async void Button_Click(...) 
{ 
    await DoSomething1(); 
    await DoSomething2(); 
    await DoSomething3(); 
} 

Теперь ваши другие методы будут выглядеть следующим образом:

async Task DoSomething1() 
{ 
    await Task.Run(() => 
    { 
     ... 
    }); 
} 

Это позволит ваш метод Button_Click асинхронно выполнения этих задач (в порядке) и держать UI отзывчивый.

+0

Пример, который я дал упрощается, чтобы сделать его более читаемым. Фактический рассматриваемый метод больше похож: «TakeLock(); RunTask(); ReleaseLock();' –

Смежные вопросы