in reply to Re^4: how to change process & thread priority on Win32 ?
in thread how to change process & thread priority on Win32 ?

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.

Replies are listed 'Best First'.
Re^6: how to change process & thread priority on Win32 ?
by rbutcher (Beadle) on Dec 10, 2005 at 00:16 UTC
    Many thanks for this excellent explanation ! I have the following code now to set the thread priority :-
    $thispid = @ARGV[0]; $threadpriority = @ARGV[1]; use Win32::API; $retcode = Win32::API->Import( 'Kernel32', q[ BOOL SetThreadPriority( HANDLE hThread, int nPriority ) ] ); print "import set threadpriority = $retcode \n"; $retcode = SetThreadPriority( $thispid, $threadpriority ); print "set threadpriority = $retcode \n";
    Value 1 is returned by Import, which I understand means OK. But value 0 is returned by SetThreadPriority. I tried using literals of 952 for the Handle of the thread I want to change and 0, 1, -1 for thread priority. Still no luck.
    Am I using the wrong format for the parameters, or is the required Handle for the thread something else, not what I see in Task Manager as PID ? I think I haven't understood the relationship of Thread Handle to PID where there is only a single thread - If I use Win32::Process to dump all the process details, it shows 952 for Handle and ProcessId - is this the real internal picture or not ?
    Is this the issue you were describing with "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." - i.e. with my code example can I only change the thread priority of perl itself ?
    thanks Rod
      Many thanks for this excellent explanation !

      Thanks, but apparently not so good as you seem to have misunderstood it. Don't feel bad about that, it probably reflects more on my explanation than anything else. I'll try to correct that here.

      PID == Process IDentifier. These are the values you see in the Task Manager. They are not the same as TID (Thread identifiers), nor are they either process or thread handles. You cannot use SetThreadPriority() to affect a processes priority.

      Each process consists of one or more threads. Each process has a PID. Each thread within a process has a TID . Each PID is (at any given time) unique within the system. Ie. No two running processes will have the same PID. TIDs are unique within their process. That is, each threaded process will have a TID==0 (zero), and any process that has more than one thread will have a TID==1 (unless it has already terminated); and so on.

      Update: The above stuck sentence is true only for TIDs returned by Perl's threads module! Which are not the same as Win32 native TIDS, which are unique within the system at any given time.

      Process and thread handles are required to use the SetPriorityClass() and SetThreadPriority() APIs respectively. And you must have the correct type for each call, and handles are different from IDs.

      IDs identify a particular instance of a process(within the system) or thread (within its process).

      Handles are created as required for a particular use. They 'point', (in a loose sense of the term), to a given process or thread, but they also carry with them a bunch of other information, like security privileges, access rights and inheritability flags. A handle of either type can only be used within the process for which it is created, and for the purpose (thread or process) for which it is created. If you obtain a handle within one process, you cannot 'pass' it to another process and use it--except if you use DuplicateHandle() to duplicate an existing handle within one process specifically for another process.

      Indeed, having duplicated a handle in one process for use in another, the duplicate is unusable within the process that created it. It must be passed to another process, and only the process for which it was duplicated, before it can be used.

      All of which may be more than you wished to know, but it is necessary background information for the answer to your real question. Which, going by the code snippet you've posted above, is nothing at all to do with "threads", despite the title of your original post.

      What you appear to be wanting to do, is to change the priority of a process, via it's PID (as listed in the Task Manager), from a separate process, namely a Perl script.

      The good news is that, if all you want to do is change the process priority of an existing process, then Win32::Process provides access to the calls required without your needing to use Win32::API. Which means that martos posts do indeed tell you how to do this, albeit that it is completely at odds with the title of your OP.

      In a nutshell, to do this you need to a) obtain a handle to the process who's priority you wish to change; b) call SetPriorityClass(). Win32::Process gives you direct access to the calls required to do both. See the snippet below.

      However, I think I see from whence your confusion stems. There are two sets of 'priority' related constants exported by Win32::Process. The *PRIORITY_CLASS set and the THREAD_PRIORITY_* set.

      • The former set are those that should be used in conjunction with SetPriorityClass(), and work as advertised.
      • The latter set, are not intended for use with that call.

        And whilst they seem to be accepted by the module, and I get no errors from the OS when I use them, they do not appear to have any affect on the target process whatsoever.

        I did get the impression from reading the MSDN docs that it might be the case that using the THREAD_PRIORITY_* constants in conjunction with SetPriorityClass() (against the process), might:

        1. Alter the priority for the base thread of that process;
        2. Or alter the default priority used for any new threads created within that process.

      But this does not appear to be the case according to my tests. Maybe it was once true and isn't on my version of the OS. There are now two new PRIORITY_CLASS constants that the module does not export (ABOVE/BELOW_NORMAL), as these were not available when the module was written.

      Basically, there appears to be no good reason that I can divine for why the THREAD_PRIORITY_* constants are exported by Win32::Process. As far as I can tell, there are no calls within that module capable of making use of them. It would probably be better if they were removed.

      Here's a snippet that allows you to set the priority class of an existing process to any of the 4 (6, depending on the age of your version) priority classes:

      realtime|high(|above)|normal(|below)|idle.

      It could really use the addition of code to look up the process by name and retrieve its PID, but there are several snippets around the site for doing that.

      #! perl -slw use strict; use Win32::Process; my %priorityClasses = ( realtime => REALTIME_PRIORITY_CLASS, high => HIGH_PRIORITY_CLASS, above => 0x00008000, ## ABOVE_NORMAL_PRIORITY_C +LASS (Not NT/95/96/ME). normal => NORMAL_PRIORITY_CLASS, below => 0x00004000, ## BELOW_NORMAL_PRIORITY_C +LASS (Not NT/95/96/ME). idle => IDLE_PRIORITY_CLASS, ); my %priority2class = reverse %priorityClasses; my( $pid, $priorityClass ) = @ARGV; $priorityClass = lc $priorityClass; die 'Priority class must be specified as [realtime|above|normal|below| +idle]' unless exists $priorityClasses{ $priorityClass }; my( $OS, $major, $minor, $build, $id ) = Win32::GetOSVersion(); die "Priority class $priorityClass not available on $OS" if $priorityClass eq 'above' or $priorityClass eq 'below' and $major < 5; my( $hProcess, $class ); Win32::Process::Open( $hProcess, $pid, 0 ) or die $^E; $hProcess->GetPriorityClass( $class ); print "Process $pid had priority class: ", $priority2class{ $class }; $hProcess->SetPriorityClass( $priorityClasses{ $priorityClass } ) or d +ie $^E; $hProcess->GetPriorityClass( $class ); print "Process $pid now has priority class: ", $priority2class{ $class + }; __END__ P:\test>SetPriority.pl 1892 high Process 1892 had priority class: normal Process 1892 now has priority class: high P:\test>SetPriority.pl 1892 normal Process 1892 had priority class: high Process 1892 now has priority class: normal

      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.
        I need to change both Process Priority and Thread Priority - I've already managed to change Process Priority using SetPriorityClass. So I'm working on changing the Thread Priority. I think my problem up to now has been my vagueness / misunderstandingson terminology - this was not helped by my using Win32::Process::Info to dump all the system process details - it gives Handle and ProcessId with same value, and I assumed Handle meant Thread Handle, and that it was the same as Process Id if the process only had a single thread (i.e. I thought Handle == PID == TID).
        But with your detailed explanation of terminology I am now a little closer to Enlightenment. As I see it now, to change a thread priority :-
        I need to generate a Duplicate Handle from within the running process.
        Use this duplicate handle from within Perl to change the Thread Priority of the original thread.
        I'll go away and use the info I now have to make it work. Many thanks for your patience.
A reply falls below the community's threshold of quality. You may see it by logging in.