В целом я нашел, что использование, TBB требует намного большего количества трудоемких изменений в кодовой базе с высокой выплатой, в то время как OpenMP дает быструю, но умеренную выплату. Если Вы смотрите новый модуль с нуля и думаете долгосрочное движение с TBB. Если Вы хотите маленькие но непосредственные усиления, идут с OpenMP.
кроме того, TBB и OpenMP не являются взаимоисключающими.
Одна из проблем этого подхода состоит в том, что очень опасно произвольно прерывать поток (практически на любом языке). Слишком много проблем, которые могут возникнуть из-за неиспользованных ресурсов и неправильно удерживаемых блокировок. Как правило, лучше всего установить какой-либо флаг, чтобы попросить поток безопасно прервать себя или забыть о потоке и позволить ему работать до завершения.
Кроме того, прерывание потока в пуле потоков очень опасно, и я считаю, что эта операция не поддерживается. Потоки в ThreadPool не принадлежат вам, и их холодное прерывание имеет серьезные последствия для ThreadPool.
Вот решение, которое я бы выбрал.
private object m_lock = new object();
private bool m_isRunning = false;
private bool m_isAbortRequested = false;
public void OnButtonClick(object sender, EventArgs e) {
lock ( m_lock ) {
if ( m_isRunning ) {
m_isAbortRequested = true;
} else {
m_isAbortRequested = false;
m_isRunning = true;
ThreadPool.QueueUserWorkItem(BackgroundMethod);
}
}
}
private void BackgroundMethod() {
try {
DoRealWork();
} finally {
lock (m_lock) {
m_isRunning = false;
}
}
}
private void DoRealWork() {
...
if ( m_isAbortRequested ) {
return;
}
}
Да, это очень неправильно. Никогда не следует пытаться вручную управлять потоком ThreadPool
. Если вам нужен такой контроль, вы должны использовать свой собственный объект Thread
. Кроме того, Abort ()
не рекомендуется для завершения потока; у вас должен быть элемент управления volatile bool
в вашей форме, который код в MethodToCall
проверяет в различных точках и корректно завершает работу, когда он истина
. Хотя вы можете использовать тот же подход с ThreadPool
, тот факт, что вам нужно иметь возможность отменить, по-видимому, указывает на то, что процесс выполняется долго или, по крайней мере, имеет потенциал. ThreadPool
не следует использовать для длительно выполняемых процессов.
Например ...
private volatile bool stopThread = false;
private Thread workThread;
private void StartThread()
{
if(workThread == null)
{
stopThread = false;
workThread = new Thread(new ThreadStart(MethodToCall));
workThread.Start();
}
}
private void StopThread()
{
if(workThread != null)
{
stopThread = true;
workThread.Join(); // This makes the code here pause until the Thread exits.
workThread = null;
}
}
Затем в MethodToCall
просто проверяйте логическое значение stopThread
через частые промежутки времени и выполняйте любые необходимые действия по очистке и выйдите из метода. Например ...
private void MethodToCall()
{
// do some work here and get to a logical stopping point
if(stopThread)
{
// clean up your work
return;
}
// do some more work and get to another stopping point
if(stopThread)
{
// clean up your work
return;
}
}
И просто повторите этот шаблон.
В ситуациях, когда одному потоку нужно «сигнализировать» другому потоку, чтобы что-то сделать, я обычно использую System.Threading.ManualResetEvent, чтобы сигнализировать второстепенному потоку об остановке, например:
private volatile bool _threadRunning = false;
private ManualResetEvent _signal = new ManualResetEvent(false);
private Thread _thread;
private void OnButtonClick(object sender, EventArgs e)
{
if (!_threadRunning) {
// Reset the 'signal' event.
_signal.Reset();
// Build your thread parameter here.
object param = ;
// Create the thread.
_thread = new Thread(ExecuteThreadLogicConditionally(param));
// Make sure the thread shuts down automatically when UI closes
_thread.IsBackground = true;
// Start the thread.
_thread.Start();
// Prevent another thread from being started.
_threadRunning = true;
} else {
// Signal the thread to stop.
_signal.Set();
// DO NOT JOIN THE THREAD HERE! If the thread takes a while
// to exit, then your UI will be frozen until it does. Just
// set the signal and move on.
}
}
// If the thread is intended to execute its logic over and over until
// stopped, use this callback.
private void ExecuteThreadLogicUntilStopped(object param)
{
// Use a while loop to prevent the thread from exiting too early.
while (!_signal.WaitOne(0)) {
// Put your thread logic here...
}
// Set the flag so anther thread can be started.
_threadRunning = false;
}
// If the thread logic is to be executed once and then wait to be
// shutdown, use this callback.
private void ExecuteThreadLogicOnce(object param)
{
// Put your thread logic here...
//
// Now wait for signal to stop.
_signal.WaitOne();
// Set the flag so another thread can be started.
_threadRunning = false;
}
// If the thread needs to be stopped at any point along the way, use
// this callback. The key here is to 'sprinkle' checks of the 'signal'
// to see if the thread should stop prematurely.
private void ExecuteThreadLogicConditionally(object param)
{
if (_signal.WaitOne(0)) { _threadRunning = false; return; }
// Execute small chunk of logic here...
if (_signal.WaitOne(0)) { _threadRunning = false; return; }
// Execute another small chuck of logic here...
if (_signal.WaitOne(0)) { _threadRunning = false; return; }
// Continue this pattern through the method.
}
Примечание что это решение вообще не использует ThreadPool. Это легко сделать. И в качестве предложения я бы не стал гадать с функцией SetMaxThreads () в ThreadPool. Просто позвольте ThreadPool делать свое дело. Он был разработан с учетом того, как вы его используете.