作法:建立延遲後完成的工作
這個範例示範如何使用 concurrency::task
、 concurrency::cancellation_token_source
、 concurrency::cancellation_token
、 concurrency::task_completion_event
、 concurrency::timer
和 concurrency::call
類別來建立延遲之後完成的工作。 您可以使用此方法來建立偶爾輪詢資料的迴圈。 您也可以介紹逾時、預先決定時間的使用者輸入延遲處理等等。
範例:complete_after和cancel_after_timeout函式
下列範例顯示 complete_after
和 cancel_after_timeout
函式。 complete_after
函式會建立在指定的延遲之後完成的 task
物件。 它會使用 timer
物件和 call
物件將 task_completion_event
物件設定在指定的延遲之後。 您可以使用 task_completion_event
類別定義在執行緒或另一個工作表示有可用值之後完成的工作。 設定事件之後,接聽程式的工作便會完成,而且其後續工作會排定繼續執行。
提示
如需 和 類別的詳細資訊 timer
,這些類別 call
是非同步代理程式程式庫的一部分,請參閱 非同步消息塊 。
函 cancel_after_timeout
式會建置在 complete_after
函式上,以在指定逾時之前未完成該工作時取消工作。 cancel_after_timeout
函式會建立兩個工作。 第一個工作表示在提供的工作完成之後成功並完成。 第二個工作表示失敗,並在指定的逾時之後完成。 cancel_after_timeout
函式會建立在成功或失敗的工作完成後執行的接續工作。 如果失敗的工作先完成,則接續工作會取消語彙基元來源以取消整個工作。
// Creates a task that completes after the specified delay.
task<void> complete_after(unsigned int timeout)
{
// A task completion event that is set when a timer fires.
task_completion_event<void> tce;
// Create a non-repeating timer.
auto fire_once = new timer<int>(timeout, 0, nullptr, false);
// Create a call object that sets the completion event after the timer fires.
auto callback = new call<int>([tce](int)
{
tce.set();
});
// Connect the timer to the callback and start the timer.
fire_once->link_target(callback);
fire_once->start();
// Create a task that completes after the completion event is set.
task<void> event_set(tce);
// Create a continuation task that cleans up resources and
// and return that continuation task.
return event_set.then([callback, fire_once]()
{
delete callback;
delete fire_once;
});
}
// Cancels the provided task after the specifed delay, if the task
// did not complete.
template<typename T>
task<T> cancel_after_timeout(task<T> t, cancellation_token_source cts, unsigned int timeout)
{
// Create a task that returns true after the specified task completes.
task<bool> success_task = t.then([](T)
{
return true;
});
// Create a task that returns false after the specified timeout.
task<bool> failure_task = complete_after(timeout).then([]
{
return false;
});
// Create a continuation task that cancels the overall task
// if the timeout task finishes first.
return (failure_task || success_task).then([t, cts](bool success)
{
if(!success)
{
// Set the cancellation token. The task that is passed as the
// t parameter should respond to the cancellation and stop
// as soon as it can.
cts.cancel();
}
// Return the original task.
return t;
});
}
範例:計算質數的計數
下列範例會多次計算範圍 [0, 100000] 中質數的計數。 如果作業未在指定的時間限制中完成,則作業會失敗。 count_primes
函式將示範如何使用 cancel_after_timeout
函式。 它會計算指定範圍內的質數,如果工作在提供的時間未完成,就會失敗。 wmain
函式會多次呼叫 count_primes
函式。 每次它都會將時間限制減半。 此程式會在作業未在目前時間限制中完成之後完成。
// Determines whether the input value is prime.
bool is_prime(int n)
{
if (n < 2)
return false;
for (int i = 2; i < n; ++i)
{
if ((n % i) == 0)
return false;
}
return true;
}
// Counts the number of primes in the range [0, max_value].
// The operation fails if it exceeds the specified timeout.
bool count_primes(unsigned int max_value, unsigned int timeout)
{
cancellation_token_source cts;
// Create a task that computes the count of prime numbers.
// The task is canceled after the specified timeout.
auto t = cancel_after_timeout(task<size_t>([max_value, timeout, cts]
{
combinable<size_t> counts;
parallel_for<unsigned int>(0, max_value + 1, [&counts, cts](unsigned int n)
{
// Respond if the overall task is cancelled by canceling
// the current task.
if (cts.get_token().is_canceled())
{
cancel_current_task();
}
// NOTE: You can replace the calls to is_canceled
// and cancel_current_task with a call to interruption_point.
// interruption_point();
// Increment the local counter if the value is prime.
if (is_prime(n))
{
counts.local()++;
}
});
// Return the sum of counts across all threads.
return counts.combine(plus<size_t>());
}, cts.get_token()), cts, timeout);
// Print the result.
try
{
auto primes = t.get();
wcout << L"Found " << primes << L" prime numbers within "
<< timeout << L" ms." << endl;
return true;
}
catch (const task_canceled&)
{
wcout << L"The task timed out." << endl;
return false;
}
}
int wmain()
{
// Compute the count of prime numbers in the range [0, 100000]
// until the operation fails.
// Each time the test succeeds, the time limit is halved.
unsigned int max = 100000;
unsigned int timeout = 5000;
bool success = true;
do
{
success = count_primes(max, timeout);
timeout /= 2;
} while (success);
}
/* Sample output:
Found 9592 prime numbers within 5000 ms.
Found 9592 prime numbers within 2500 ms.
Found 9592 prime numbers within 1250 ms.
Found 9592 prime numbers within 625 ms.
The task timed out.
*/
當您使用這項技術在延遲之後取消工作時,任何未啟動的工作都不會在取消整體工作之後開始。 不過,對於任何長時間執行的工作來說,回應取消非常重要。 如需工作取消的詳細資訊,請參閱 PPL 中的取消。
完成程式碼範例
以下是這個範例的完整程式碼:
// task-delay.cpp
// compile with: /EHsc
#include <ppl.h>
#include <ppltasks.h>
#include <agents.h>
#include <iostream>
using namespace concurrency;
using namespace std;
// Creates a task that completes after the specified delay.
task<void> complete_after(unsigned int timeout)
{
// A task completion event that is set when a timer fires.
task_completion_event<void> tce;
// Create a non-repeating timer.
auto fire_once = new timer<int>(timeout, 0, nullptr, false);
// Create a call object that sets the completion event after the timer fires.
auto callback = new call<int>([tce](int)
{
tce.set();
});
// Connect the timer to the callback and start the timer.
fire_once->link_target(callback);
fire_once->start();
// Create a task that completes after the completion event is set.
task<void> event_set(tce);
// Create a continuation task that cleans up resources and
// and return that continuation task.
return event_set.then([callback, fire_once]()
{
delete callback;
delete fire_once;
});
}
// Cancels the provided task after the specifed delay, if the task
// did not complete.
template<typename T>
task<T> cancel_after_timeout(task<T> t, cancellation_token_source cts, unsigned int timeout)
{
// Create a task that returns true after the specified task completes.
task<bool> success_task = t.then([](T)
{
return true;
});
// Create a task that returns false after the specified timeout.
task<bool> failure_task = complete_after(timeout).then([]
{
return false;
});
// Create a continuation task that cancels the overall task
// if the timeout task finishes first.
return (failure_task || success_task).then([t, cts](bool success)
{
if(!success)
{
// Set the cancellation token. The task that is passed as the
// t parameter should respond to the cancellation and stop
// as soon as it can.
cts.cancel();
}
// Return the original task.
return t;
});
}
// Determines whether the input value is prime.
bool is_prime(int n)
{
if (n < 2)
return false;
for (int i = 2; i < n; ++i)
{
if ((n % i) == 0)
return false;
}
return true;
}
// Counts the number of primes in the range [0, max_value].
// The operation fails if it exceeds the specified timeout.
bool count_primes(unsigned int max_value, unsigned int timeout)
{
cancellation_token_source cts;
// Create a task that computes the count of prime numbers.
// The task is canceled after the specified timeout.
auto t = cancel_after_timeout(task<size_t>([max_value, timeout, cts]
{
combinable<size_t> counts;
parallel_for<unsigned int>(0, max_value + 1, [&counts, cts](unsigned int n)
{
// Respond if the overall task is cancelled by canceling
// the current task.
if (cts.get_token().is_canceled())
{
cancel_current_task();
}
// NOTE: You can replace the calls to is_canceled
// and cancel_current_task with a call to interruption_point.
// interruption_point();
// Increment the local counter if the value is prime.
if (is_prime(n))
{
counts.local()++;
}
});
// Return the sum of counts across all threads.
return counts.combine(plus<size_t>());
}, cts.get_token()), cts, timeout);
// Print the result.
try
{
auto primes = t.get();
wcout << L"Found " << primes << L" prime numbers within "
<< timeout << L" ms." << endl;
return true;
}
catch (const task_canceled&)
{
wcout << L"The task timed out." << endl;
return false;
}
}
int wmain()
{
// Compute the count of prime numbers in the range [0, 100000]
// until the operation fails.
// Each time the test succeeds, the time limit is halved.
unsigned int max = 100000;
unsigned int timeout = 5000;
bool success = true;
do
{
success = count_primes(max, timeout);
timeout /= 2;
} while (success);
}
/* Sample output:
Found 9592 prime numbers within 5000 ms.
Found 9592 prime numbers within 2500 ms.
Found 9592 prime numbers within 1250 ms.
Found 9592 prime numbers within 625 ms.
The task timed out.
*/
編譯程式碼
若要編譯器代碼,請複製程式碼,然後將它貼到 Visual Studio 專案中,或貼到名為 task-delay.cpp
的檔案中,然後在 Visual Studio 命令提示字元視窗中執行下列命令。
cl.exe /EHsc task-delay.cpp
另請參閱
工作平行處理原則
task 類別 (並行執行階段)
cancellation_token_source 類別
cancellation_token 類別
task_completion_event 類別
timer 類別
call 類別
非同步訊息區
PPL 中的取消