Это возможно, но, учитывая предоставленную вами информацию, она кажется бессмысленной ... вы принудительно выполняете однопоточное выполнение внешней программы, так что вы могли бы иметь один поток, выполняющий ее. Если Thread 2
нужно дождаться Thread 1
, чтобы запустить «внешнюю программу», просто позвольте Thread 1
выполнить всю работу, так как он уже знает, когда запустил «внешнюю программу».
Единственное преимущество, которое вы получите от многопоточного подхода, - это если у вас есть куча обработки, которую вы должны выполнить перед выполнением «внешней программы», и эта обработка должна быть хорошим кандидатом для одновременного выполнения.
Обновление
ОК, есть пара способов сделать это только с одним дополнительным потоком, чтобы обеспечить адаптацию потока основного / графического интерфейса. Первый подход - это простая блокировка вокруг внешнего ресурса, с которым вы взаимодействуете:
public class ExternalResourceHandler
{
private readonly ExternalResource _resource;
private readonly object _sync = new object();
// constructors
// ...
// other methods
public void PerformExternalOperation()
{
lock(_sync)
{
Result result = _resource.Execute();
// do soemthing with the result
}
}
}
Вот 3 многопоточных версии для выполнения кода:
- Использование метода
Parallel.For
: рекомендуется, если выполнение внешней программы занимает короткое время - я бы рекомендовал для вещей менее 25 секунд (хотя это не обязательно «правильное» число).
- Использование
ThreadPool
: опять же, я бы рекомендовал для вещей, которые занимают менее 25 секунд (с тем же резервированием, что и выше).
- Использование
Thread
: это было бы рекомендовано, если операция выполняется дольше (т. Е. Более 25 секунд, но было бы так же хорошо, если бы она была менее 25 секунд).
Вот несколько примеров (не обязательно функциональных, в основном предназначенных для того, чтобы дать вам представление о различных подходах):
public class Program
{
public static ExternalResourceHandler _erh = new ExternalResourceHandler();
static int Main()
{
Console.WriteLine("Type 'exit' to stop; 'parallel', 'pool' or 'thread' for the corresponding execution version.");
string input = Console.ReadLine();
while(input != "exit")
{
switch(input)
{
case "parallel":
// Run the Parallel.For version
ParallelForVersion();
break;
caase "pool":
// Run the threadpool version
ThreadPoolVersion();
break;
case "thread":
// Run the thread version
ThreadVersion();
break;
default:
break;
}
input = Console.ReadLine();
}
return 0;
}
public static void ParallelForVersion()
{
Parallel.For(0, 1, i =>
{
_erh.PerformExternalOperation();
});
}
public static void ThreadPoolVersion()
{
ThreadPool.QueueUserWorkItem(o=>
{
_erh.PerformExternalOperation();
});
}
public static void ThreadVersion()
{
Thread t = new Thread(()=>
{
_erh.PerformExternalOperation();
});
t.IsBackground = true;
t.Start();
}
}
Другой вариант - использовать шаблон проектирования «Производитель / Потребитель», где ваш ExternalResourceHandler
является потребителем и обрабатывает запросы к внешнему ресурсу из поточно-ориентированной очереди. Ваш основной поток просто помещает запросы в очередь и немедленно возвращается к работе. Вот пример:
public class ExternalResourceHandler
{
private volatile boolean _running;
private readonly ExternalResource _resource;
private readonly BlockingQueue<Request> _requestQueue;
public ExternalResourceHandler( BlockingQueue<Request> requestQueue)
{
_requestQueue = requestQueue;
_running = false;
}
public void QueueRequest(Request request)
{
_requestQueue.Enqueue(request);
}
public void Run()
{
_running = true;
while(_running)
{
Request request = null;
if(_requestQueue.TryDequeue(ref request) && request!=null)
{
_resource.Execute(request);
}
}
}
// methods to stop the handler (i.e. set the _running flag to false)
}
Ваш главный будет выглядеть так:
public class Program
{
public static ExternalResourceHandler _erh = new ExternalResourceHandler();
static int Main()
{
Thread erhThread = new Thread(()=>{_erh.Run();});
erhThread.IsBackground = true;
erhThread.Start();
Console.WriteLine("Type 'exit' to stop or press enter to enqueue another request.");
string input = Console.ReadLine();
while(input != "exit")
{
_erh.EnqeueRequest(new Request());
input = Console.ReadLine();
}
// Stops the erh by setting the running flag to false
_erh.Stop();
// You may also need to interrupt the thread in order to
// get it out of a blocking state prior to calling Join()
erhThread.Join();
return 0;
}
}
Как видите: в обоих случаях вся работа для внешнего обработчика выполняется в одном потоке, но ваш основной поток все еще остается отзывчивым.