如何:建立使用特定排程器原則的代理程式
代理程式是應用程式元件,可與其他元件異步運作,以解決較大的運算工作。 代理程式通常會有設定生命週期並維護狀態。
每個代理程式都可以有唯一的應用程式需求。 例如,啟用用戶互動的代理程式(擷取輸入或顯示輸出)可能需要較高優先順序的計算資源存取權。 排程器原則可讓您控制排程器在管理工作時所使用的策略。 本主題示範如何建立使用特定排程器原則的代理程式。
如需搭配異步消息區塊使用自定義排程器原則的基本範例,請參閱 如何:指定特定排程器原則。
本主題使用異步代理程序連結庫的功能,例如代理程式、消息塊和訊息傳遞函式,來執行工作。 如需異步代理程序連結庫的詳細資訊,請參閱 異步代理程序連結庫。
範例
下列範例會定義衍生自 併行::agent: permutor
和 printer
的兩個類別。 類別 permutor
會計算指定輸入字串的所有排列。 類別 printer
會將進度訊息列印至主控台。 類別 permutor
會執行需要大量計算的運算作業,而此作業可能會使用所有可用的運算資源。 為了有用,類別 printer
必須及時列印每個進度訊息。
為了提供printer
計算資源的公平存取權,此範例會使用如何:管理排程器實例中所述的步驟,建立具有自定義原則的排程器實例。 自定義原則會將線程優先順序指定為最高優先順序類別。
為了說明使用具有自定義原則的排程器的優點,此範例會執行整體工作兩次。 此範例會先使用預設排程器來排程這兩個工作。 然後,此範例會使用預設排程器來排程 permutor
物件,以及具有自定義原則的排程器來排程 printer
物件。
// permute-strings.cpp
// compile with: /EHsc
#include <windows.h>
#include <ppl.h>
#include <agents.h>
#include <iostream>
#include <sstream>
using namespace concurrency;
using namespace std;
// Computes all permutations of a given input string.
class permutor : public agent
{
public:
explicit permutor(ISource<wstring>& source,
ITarget<unsigned int>& progress)
: _source(source)
, _progress(progress)
{
}
explicit permutor(ISource<wstring>& source,
ITarget<unsigned int>& progress,
Scheduler& scheduler)
: agent(scheduler)
, _source(source)
, _progress(progress)
{
}
explicit permutor(ISource<wstring>& source,
ITarget<unsigned int>& progress,
ScheduleGroup& group)
: agent(group)
, _source(source)
, _progress(progress)
{
}
protected:
// Performs the work of the agent.
void run()
{
// Read the source string from the buffer.
wstring s = receive(_source);
// Compute all permutations.
permute(s);
// Set the status of the agent to agent_done.
done();
}
// Computes the factorial of the given value.
unsigned int factorial(unsigned int n)
{
if (n == 0)
return 0;
if (n == 1)
return 1;
return n * factorial(n - 1);
}
// Computes the nth permutation of the given wstring.
wstring permutation(int n, const wstring& s)
{
wstring t(s);
size_t len = t.length();
for (unsigned int i = 2; i < len; ++i)
{
swap(t[n % i], t[i]);
n = n / i;
}
return t;
}
// Computes all permutations of the given string.
void permute(const wstring& s)
{
// The factorial gives us the number of permutations.
unsigned int permutation_count = factorial(s.length());
// The number of computed permutations.
LONG count = 0L;
// Tracks the previous percentage so that we only send the percentage
// when it changes.
unsigned int previous_percent = 0u;
// Send initial progress message.
send(_progress, previous_percent);
// Compute all permutations in parallel.
parallel_for (0u, permutation_count, [&](unsigned int i) {
// Compute the permutation.
permutation(i, s);
// Send the updated status to the progress reader.
unsigned int percent = 100 * InterlockedIncrement(&count) / permutation_count;
if (percent > previous_percent)
{
send(_progress, percent);
previous_percent = percent;
}
});
// Send final progress message.
send(_progress, 100u);
}
private:
// The buffer that contains the source string to permute.
ISource<wstring>& _source;
// The buffer to write progress status to.
ITarget<unsigned int>& _progress;
};
// Prints progress messages to the console.
class printer : public agent
{
public:
explicit printer(ISource<wstring>& source,
ISource<unsigned int>& progress)
: _source(source)
, _progress(progress)
{
}
explicit printer(ISource<wstring>& source,
ISource<unsigned int>& progress, Scheduler& scheduler)
: agent(scheduler)
, _source(source)
, _progress(progress)
{
}
explicit printer(ISource<wstring>& source,
ISource<unsigned int>& progress, ScheduleGroup& group)
: agent(group)
, _source(source)
, _progress(progress)
{
}
protected:
// Performs the work of the agent.
void run()
{
// Read the source string from the buffer and print a message.
wstringstream ss;
ss << L"Computing all permutations of '" << receive(_source) << L"'..." << endl;
wcout << ss.str();
// Print each progress message.
unsigned int previous_progress = 0u;
while (true)
{
unsigned int progress = receive(_progress);
if (progress > previous_progress || progress == 0u)
{
wstringstream ss;
ss << L'\r' << progress << L"% complete...";
wcout << ss.str();
previous_progress = progress;
}
if (progress == 100)
break;
}
wcout << endl;
// Set the status of the agent to agent_done.
done();
}
private:
// The buffer that contains the source string to permute.
ISource<wstring>& _source;
// The buffer that contains progress status.
ISource<unsigned int>& _progress;
};
// Computes all permutations of the given string.
void permute_string(const wstring& source,
Scheduler& permutor_scheduler, Scheduler& printer_scheduler)
{
// Message buffer that contains the source string.
// The permutor and printer agents both read from this buffer.
single_assignment<wstring> source_string;
// Message buffer that contains the progress status.
// The permutor agent writes to this buffer and the printer agent reads
// from this buffer.
unbounded_buffer<unsigned int> progress;
// Create the agents with the appropriate schedulers.
permutor agent1(source_string, progress, permutor_scheduler);
printer agent2(source_string, progress, printer_scheduler);
// Start the agents.
agent1.start();
agent2.start();
// Write the source string to the message buffer. This will unblock the agents.
send(source_string, source);
// Wait for both agents to finish.
agent::wait(&agent1);
agent::wait(&agent2);
}
int wmain()
{
const wstring source(L"Grapefruit");
// Compute all permutations on the default scheduler.
Scheduler* default_scheduler = CurrentScheduler::Get();
wcout << L"With default scheduler: " << endl;
permute_string(source, *default_scheduler, *default_scheduler);
wcout << endl;
// Compute all permutations again. This time, provide a scheduler that
// has higher context priority to the printer agent.
SchedulerPolicy printer_policy(1, ContextPriority, THREAD_PRIORITY_HIGHEST);
Scheduler* printer_scheduler = Scheduler::Create(printer_policy);
// Register to be notified when the scheduler shuts down.
HANDLE hShutdownEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
printer_scheduler->RegisterShutdownEvent(hShutdownEvent);
wcout << L"With higher context priority: " << endl;
permute_string(source, *default_scheduler, *printer_scheduler);
wcout << endl;
// Release the printer scheduler.
printer_scheduler->Release();
// Wait for the scheduler to shut down and destroy itself.
WaitForSingleObject(hShutdownEvent, INFINITE);
// Close the event handle.
CloseHandle(hShutdownEvent);
}
此範例會產生下列輸出。
With default scheduler:
Computing all permutations of 'Grapefruit'...
100% complete...
With higher context priority:
Computing all permutations of 'Grapefruit'...
100% complete...
雖然這兩組工作會產生相同的結果,但使用自定義原則的版本可讓 printer
物件以較高的優先順序執行,使其行為更具回應性。
編譯程式碼
複製範例程式代碼,並將其貼到 Visual Studio 專案中,或貼到名為 permute-strings.cpp
的檔案中,然後在 Visual Studio 命令提示字元視窗中執行下列命令。
cl.exe /EHsc permute-strings.cpp