You are correct. The calls in Win32::Process are Process specific and have nothing to do with threads at all.
To set thread priorities you will need to use Win32::API to gain access to SetThreadPriority(). That is easy enough to do, but there is a problem.
In order to use that call, you will need a thread handle. Unfortunately, the tid returned by threads->tid() is not a native thread handle, but rather a Perl internal identifier, and there is no provision for obtaining the former from the latter.
However, there is a native API that will return the handle for the currently running thread GetCurrentThread() and you can get at that through Win32::API.
Provided that you only want each thread to be able to alter it's own priority, that works nicely, but if you want a central thread to be able to adjust the priorities of other threads, then you will have to set up a mapping between threads->tids and Win32 Thread handles.
One way to do that would be to create a shared hash and have each of your threads query it's own thread handle and save it in the hash keyed by their threads->tid, or perhaps something that identifies the thread by what it does.
Unfortunately, the handle returned by GetCurrentThread() is not a real handle, but a psuedo-handle (-2) that can only be used within that thread. To be able to get a handle usable by other threads, it is necessary to duplicate the handle using DuplicateHandle() (D'uh:). Putting that all together, you arrive at something like this.
The following snippet sets 7 threads running, has them query their thread handles, duplicate them for use in other threads and set up the tid->handle mapping in a shared hash.
The main thread then sets their priorities, one at each of those possible, before setting the $go flag and allowing them to run for 10 seconds. Once they get the off, each thread attempts to relinguish its timeslice 2 million times, counting them as it does so; before printing out its priority and the count of timeslices it was allocated before it reached 2 million or ran out of time.
It shows that higher priority threads will starve lower ones (almost) completely of cpu whilst they are able to run, hence the lower priority threads show almost no activity.
Output
P:\test>512584
1 started h(100)
2 started h(124)
3 started h(136)
4 started h(148)
5 started h(172)
6 started h(184)
7 started h(196)
tid(7) priority(15) received 2000000 timeslices
tid(6) priority(2) received 2000000 timeslices
tid(5) priority(1) received 2000000 timeslices
tid(4) priority(0) received 342158 timeslices
tid(3) priority(-1) received 3 timeslices
tid(2) priority(-2) received 2 timeslices
tid(1) priority(-15) received 2 timeslices
However, if you make the sleep in the main thread loop 1 millisecond instead of 0, then during that 1 millisecond, lower priority threads are able to run, with the result that all threads receive an almost equal share of the processor.
P:\test>512584
1 started h(104)
2 started h(132)
3 started h(144)
4 started h(156)
5 started h(180)
6 started h(192)
7 started h(204)
tid(2) priority(-2) received 5087 timeslices
tid(1) priority(-15) received 5086 timeslices
tid(7) priority(15) received 5121 timeslices
tid(6) priority(2) received 5120 timeslices
tid(5) priority(1) received 5117 timeslices
tid(4) priority(0) received 5114 timeslices
tid(3) priority(-1) received 5099 timeslices
As all the threads are sleeping most of the time, there are only a few occasions when a higher priority thread is eligible to run at the same time as a lower one and therefore usurps its timeslice.
The code #! perl -slw
use strict;
use threads;
use threads::shared;
use Win32::API;
$| = 1;
Win32::API->Import( 'Kernel32', q[
BOOL DuplicateHandle( HANDLE hSourceProcessHandle,
HANDLE hSourceHandle, HANDLE hTargetProcessHandle,
LPHANDLE lpTargethandle, DWORD dwDesiredAccess,
BOOL bInheritHandle, DWORD dwOptions )
] ) or die $^E;
Win32::API->Import( 'Kernel32', q[ HANDLE GetCurrentProcess() ] ) or d
+ie $^E;
Win32::API->Import( 'Kernel32', q[ HANDLE GetCurrentThread() ] ) or di
+e $^E;
Win32::API->Import( 'Kernel32',
q[ BOOL SetThreadPriority( HANDLE hThread, int nPriority ) ] ) or
+die $^E;
Win32::API->Import( 'Kernel32',
q[ int GetThreadPriority( HANDLE hThread ) ] ) or die $^E;
my %threads : shared;
my $running : shared = 0;
my $go : shared = 0;
sub thread{
my $tid = threads->self->tid;
my $hProc = GetCurrentProcess();
my $hThread;
DuplicateHandle( $hProc, GetCurrentThread(), $hProc, $hThread, 0,
+0, 2 );
$threads{ $tid } = $hThread;
print "$tid started h($hThread)";
++$running;
my $count = 0;
Win32::Sleep 1 until $go;
for ( 1 .. 2_000_000 ) {
$count++ and Win32::Sleep 1;
last if !$go;
}
printf "tid($tid) priority(%d) received $count timeslices\n",
GetThreadPriority( GetCurrentThread() );
--$running;
}
my @priorities = ( -15, -2, -1, 0, 1, 2, +15 );
my @threads = map{ threads->create( \&thread ) } 0 .. $#priorities;
sleep 1 until $running == @threads;
SetThreadPriority( $threads{ $_ + 1 }, $priorities[ $_ ] )
or die $^E for 0 .. $#priorities;
$go = 1;
sleep 10;
$go = 0;
sleep 1 while $running;
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
|