ある特定のガイドラインに従えば、スレッド化されたコードをプラグインで利用できます。以下にあげるのは、この場合の注意事項です。
MThreadPool は、タスクの割り当てが可能なスレッド プールへのアクセスを提供します。タスクの数は、スレッドの数と同じである必要はありません。実際、負荷を分散させるには、スレッド数よりもタスク数の方が多いことが望ましいです。Maya では、最大限に効率化するために、複数のスレッドの間で内部的に作業が分散されます。プール内のスレッドの数は、論理プロセスの数と同じです。使用のたびにスレッド プールを削除する必要はありません。むしろ、平行領域が完了したときにスレッドは休止状態に入る、つまりスレッドはすばやく再起動できるので、パフォーマンス上の理由から言っても削除はしないことをお勧めします。
MThreadAsync では、長期間実行可能なスレッドを 1 つまたは複数個作成できます。これらは MThreadPool により作成および管理されるスレッド プールから導出されたものではなく、独立したスレッドです。これらのスレッドを使用すると、タスクを長時間実行できます。これらはスレッド プールから作成されるものではないので、サブスクリプションの超過(活動中のスレッド数が使用可能なハードウェア リソースを超過している状態)を避けるためにも、このようなスレッドの数や負荷は慎重に管理する必要があります。
MMutexLock はロッキング プリミティブで、MThreadPool スレッド、および MThreadAsync スレッドの両方と共に使用できます。スレッドの標準 Mutex ロッキングが可能です。
MSpinLock はスピン待機するロックなので、ロックが非常に短時間維持されるような状況では、Mutex ロックよりも効率的です。ただし、ロックはスピン待機されるため、大量の CPU が消費されますから、長時間、ロックしたままになるような状況では使用すべきではありません。
次の例は、シリアルおよびスレッド化アプローチを使用して、素数を検索する方法を示しています。スレッド化アプローチでは、MThreadPool クラスが使用されています。
#include <math.h>
#include <maya/MIOStream.h>
#include <maya/MSimple.h>
#include <maya/MTimer.h>
#include <maya/MGlobal.h>
#include <maya/MThreadPool.h>
DeclareSimpleCommand( threadTestCmd, PLUGIN_COMPANY, "2009");
typedef struct _threadDataTag
{
int threadNo;
long primesFound;
long start, end;
} threadData;
typedef struct _taskDataTag
{
long start, end, totalPrimes;
} taskData;
#define NUM_TASKS 16
// No global information used in function
static bool TestForPrime(int val)
{
int limit, factor = 3;
limit = (long)(sqrtf((float)val)+0.5f);
while( (factor <= limit) && (val % factor))
factor ++;
return (factor > limit);
}
// Primes finder. This function is called from multiple threads
MThreadRetVal Primes(void *data)
{
threadData *myData = (threadData *)data;
for( int i = myData->start + myData->threadNo*2; i <= myData->end; i += 2*NUM_TASKS )
{
if( TestForPrime(i) )
myData->primesFound++;
}
return (MThreadRetVal)0;
}
// Function to create thread tasks
void DecomposePrimes(void *data, MThreadRootTask *root)
{
taskData *taskD = (taskData *)data;
threadData tdata[NUM_TASKS];
for( int i = 0; i < NUM_TASKS; ++i )
{
tdata[i].threadNo = i;
tdata[i].primesFound = 0;
tdata[i].start = taskD->start;
tdata[i].end = taskD->end;
MThreadPool::createTask(Primes, (void *)&tdata[i], root);
}
MThreadPool::executeAndJoin(root);
for( int i = 0; i < NUM_TASKS; ++i )
{
taskD->totalPrimes += tdata[i].primesFound;
}
}
// Single threaded calculation
int SerialPrimes(int start, int end)
{
int primesFound = 0;
for( int i = start; i <= end; i+=2)
{
if( TestForPrime(i) )
primesFound++;
}
return primesFound;
}
// Set up and tear down parallel tasks
int ParallelPrimes(int start, int end)
{
MStatus stat = MThreadPool::init();
if( MStatus::kSuccess != stat ) {
MString str = MString("Error creating threadpool");
MGlobal::displayError(str);
return 0;
}
taskData tdata;
tdata.totalPrimes = 0;
tdata.start = start;
tdata.end = end;
MThreadPool::newParallelRegion(DecomposePrimes, (void *)&tdata);
// pool is reference counted. Release reference to current thread instance
MThreadPool::release();
// release reference to whole pool which deletes all threads
MThreadPool::release();
return tdata.totalPrimes;
}
// MSimple command that invokes the serial and parallel thread calculations
MStatus threadTestCmd::doIt( const MArgList& args )
{
MString introStr = MString("Computation of primes using the Maya API");
MGlobal::displayInfo(introStr);
if(args.length() != 2) {
MString str = MString("Invalid number of arguments, usage: threadTestCmd 1 10000");
MGlobal::displayError(str);
return MStatus::kFailure;
}
MStatus stat;
int start = args.asInt( 0, &stat );
if ( MS::kSuccess != stat ) {
MString str = MString("Invalid argument 1, usage: threadTestCmd 1 10000");
MGlobal::displayError(str);
return MStatus::kFailure;
}
int end = args.asInt( 1, &stat );
if ( MS::kSuccess != stat ) {
MString str = MString("Invalid argument 2, usage: threadTestCmd 1 10000");
MGlobal::displayError(str);
return MStatus::kFailure;
}
// start search on an odd number
if((start % 2) == 0 ) start++;
// run single threaded
MTimer timer;
timer.beginTimer();
int serialPrimes = SerialPrimes(start, end);
timer.endTimer();
double serialTime = timer.elapsedTime();
// run multithreaded
timer.beginTimer();
int parallelPrimes = ParallelPrimes(start, end);
timer.endTimer();
double parallelTime = timer.elapsedTime();
// check for correctness
if ( serialPrimes != parallelPrimes ) {
MString str("Error: Computations inconsistent");
MGlobal::displayError(str);
return MStatus::kFailure;
}
// print results
double ratio = serialTime/parallelTime;
MString str = MString("\nElapsed time for serial computation: ") + serialTime + MString("s\n");
str += MString("Elapsed time for parallel computation: ") + parallelTime + MString("s\n");
str += MString("Speedup: ") + ratio + MString("x\n");
MGlobal::displayInfo(str);
return MStatus::kSuccess;
}