Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Re^2: multi-threaded win32::console

by goibhniu (Hermit)
on Aug 14, 2007 at 00:18 UTC ( [id://632345]=note: print w/replies, xml ) Need Help??


in reply to Re: multi-threaded win32::console
in thread multi-threaded win32::console

First variant of my full code. my $CONSOLE is in main and my $threadCONSOLE is in the thread.

#/usr/bin/perl -w use strict; use threads; use threads::shared; use Thread::Queue; use Win32::Console; use Hash::Util qw(lock_keys unlock_keys lock_value unlock_value lock_hash unlock_hash hash_seed); my $i; #looper my $CONSOLE = Win32::Console->new(STD_OUTPUT_HANDLE); $CONSOLE->Title("This is a title"); #&share($CONSOLE); #$CONSOLE->Cls; my $CURRENT_ROW = 3; my $BOTTOM_ROW = 8; my @consoleInfo = $CONSOLE->Info(); my $statusRow = min ( $consoleInfo[$CURRENT_ROW], $consoleInfo[$BOTTOM +_ROW]); sub min { my ($a,$b) = @_; return ($a<=$b?$a:$b); } ###################################################################### +################### # # data structure is an array of hashes # each element in the array has all the info needed for a thread to +work # hash keys are: # 'CT' contins CipherText # 'Pattern' contains a string that will be turned into a pattern + to look for words in a wordlist # 'Solutions' contain dummy answers used for stub testing - not +needed in steady state # hashes are locked - this data need never change and if it does, th +at's a problem # ###################################################################### +################### my @aPattHashes = ( # single element hashes that associate CT with pat +terns (from ART) # {'CT'=>'LBCJC', + # 'Pattern'=>'($L)(?!\1)($B)(?!\1)(?!\2)($C)(?!\1)(?!\2)(?!\3)($J)\3 +', # 'Solutions'=>{('there','where','wasps','suede')} + # }, + # {'CT'=>'ABCJC', + # 'Pattern'=>'($A)(?!\1)($B)(?!\1)(?!\2)($C)(?!\1)(?!\2)(?!\3)($J)\3 +', # 'Solutions'=>{('there','where','wasps','suede')} + # }, + # {'CT'=>'ABC', + # 'Pattern'=>'($A)(?!\1)($B)(?!\1)(?!\2)($C)', + # 'Solutions'=>{('the','are','who','zap')} + # }, + # {'CT'=>'VJC', + # 'Pattern'=>'($V)(?!\1)($J)(?!\1)(?!\2)($C)', + # 'Solutions'=>{('the','are','who','zap')} + # } + # ); + # for ($i=0; $i <= $#aPattHashes; $i++) { + # lock_hash(%{$aPattHashes[$i]}); + # } + # ###################################################################### +################### #set up dictionary my $words = ''; open (WORDS, 'words.knu'); { local $/; $words = <WORDS>; } close WORDS; my @wordlist = split(/\n/,$words); my @DataQueues; #There are n+1 queues (mileposts vs. miles). for ($i = 0; $i <= $#aPattHashes + 1; $i++) { $DataQueues[$i] = Thread::Queue->new; } my @aThreads; # one thread per pattern. for ($i=0; $i <= $#aPattHashes ; $i++) { # each thread needs code to do, an input queue, an out +put queue, threadPattHash $aThreads[$i] = threads->new(\&wordfinder, $DataQueues[$i], $DataQu +eues[$i+1], $aPattHashes[$i], $i); } #put first (empty) solution on the first queue my $hrefSolution = &share({}); $DataQueues[0]->enqueue($hrefSolution); $DataQueues[0]->enqueue(undef); #wait for threads to exit. for ($i=0; $i<=$#aThreads; $i++) { $aThreads[$i]->join; #print "thread $i ended \n"; # this will be detected in the order +joined, not the order the threads end } $CONSOLE->Free(); #dequeue from the last queue of the chain my $Solutionref; my %Solution; print "\nresults: \n"; while ($Solutionref = $DataQueues[$#DataQueues]->dequeue) { %Solution = %{$Solutionref}; #printKeys (\%Solution); printSolution (\%Solution); } print "\n"; sub printKeys { my $href = shift; print "\nCT: "; print(($_).' ') foreach (sort keys %{$href}); print "\npt: "; print(($$href{$_}).' ') foreach (sort keys %{$href}); print "\n"; } sub printSolution { my $href = shift; my $CT=''; my $pt=''; for ($i=0; $i<=$#aPattHashes; $i++) { $CT = $CT.sprintf $aPattHashes[$i]{'CT'}.' '; $pt = $CT; } foreach (keys %{$href}) { $pt =~ s/$_/$$href{$_}/g; } #print "\n"; #print $CT."\n"; print $pt."\n"; } sub template2patt { my ($href, $template) = @_; my $patt = $template; foreach ( keys %{$href} ){ $patt =~ s/\$$_/$$href{$_}/g; } $patt =~ s/\$[A-Z]/\[a-z\]/g; return $patt; } sub compare { #direction matters? my ($hrefA, $hrefB) = @_; my @Avalues = values (%{$hrefA}); my @Bvalues = values (%{$hrefB}); my $true = 1; my $false = !$true; my $result = $true; my $k; foreach $k (keys(%{$hrefA})) { if (exists $hrefB->{$k}) { if ($hrefA->{$k} != $hrefB->{$k}) { # same key, different +values $result = $false; last; } } else { if (grep (/$hrefA->{$k}/ , ((),values (%{$hrefB}))) ) { #s +ame values, different keys $result = $false; last; } } } if ($result) { foreach (keys(%{$hrefB})) { if (exists $hrefA->{$k}) { # <<< not sure I need to check +this both directions. if ($hrefB->{$k} != $hrefA->{$k}) { # same key, differ +ent values $result = $false; last; } } else { # <<< but I definitely need to +check this both directions if (grep ($hrefB->{$k}, values (%{$hrefA}) ) ){ #same +values, different keys print 'same values, different keys: '."$k".'->'."$ +hrefB->{$k}\n"; $result = $false; last; } } } } return $result ; } sub wordfinder { my ($inqueue, $outqueue, $threadPattHash, $threadNum) = @_; my $threadCT = $$threadPattHash{CT}; my @CTchars = split(//,$threadCT); my $threadname = $threadCT; #print "$threadname started.\n"; my $patternString = $$threadPattHash{Pattern}; my $inPattQty = 0; my $outPattQty = 0; my $statString; my $threadStatusColumn = $threadNum * 12; my $threadCONSOLE = Win32::Console->new(STD_OUTPUT_HANDLE); my $pattern; my $word; my $inSolutionref; my %inSolution; my $matchSolutionref; my %matchSolution; my $outSolutionref; my %outSolution; while ($inSolutionref = $inqueue->dequeue) { $statString = sprintf ("%5s/%-5s", $inPattQty++, $outPattQty); $threadCONSOLE->WriteChar($statString, $threadStatusColumn, $s +tatusRow); $pattern = template2patt ($inSolutionref, $patternString); %inSolution = %{$inSolutionref}; foreach $word (@wordlist) { if ($word =~m/^${pattern}$/) { chomp($word); $outSolutionref = &share({}); %{$matchSolutionref} = map { $CTchars[$_] => (split(// +,$word))[$_] } (0..$#CTchars); if ( compare($inSolutionref, $matchSolutionref) ) { %$outSolutionref = (%inSolution); foreach (keys(%{$matchSolutionref})) { $outSolutio +nref->{$_} = $matchSolutionref->{$_} }; # printSolution $outSolutionref; $outqueue->enqueue($outSolutionref); $statString = sprintf ("%5s/%-5s", $inPattQty, $ou +tPattQty++); $threadCONSOLE->WriteChar($statString, $threadStat +usColumn, $statusRow); } } } } $outqueue->enqueue(undef); $statString = sprintf ("%5s/%-5s.", $inPattQty, $outPattQty); $threadCONSOLE->WriteChar($statString, $threadStatusColumn, $statu +sRow) #print "$threadname ended.\n"; }

This is to help out in a cryptography hobby. To see it really work, you'll need a copy of Knuth's word list (words.knu). I got it from the ACA web site.

Put it in the same folder and run this. The first word pattern will have (on my word list) 177 matches. After that, the output stops and looks like this:

C:\chas_sandbox>test.pl 1/177 . 3/8 4/4 3/16 C:\chas_sandbox>

This show the first thread taking one solution off it's input queue, putting 177 on it's output queue and stopping (the period). The second thread only got 3 Solutions out of those 177 before all output stopped (after the first thread ended).

If I remove all references to Win32::Console, it proceeds to completion and shows a list of possible solutions for this four word cryptogram.

Replies are listed 'Best First'.
Re^3: multi-threaded win32::console
by BrowserUk (Patriarch) on Aug 14, 2007 at 01:21 UTC

    When I run the above, I get reams and reams of

    Use of uninitialized value in exists at C:\test\632320.pl line 161. Use of uninitialized value in null operation at C:\test\632320.pl line + 161. Use of uninitialized value in hash element at C:\test\632320.pl line 1 +67. Use of uninitialized value in hash element at C:\test\632320.pl line 1 +67. Use of uninitialized value in hash element at C:\test\632320.pl line 1 +67. Use of uninitialized value in hash element at C:\test\632320.pl line 1 +67. Use of uninitialized value in exists at C:\test\632320.pl line 161. ...

    It's not at all clear to me what a 'null operation' is, (I don't think I ever encountered that one before :), but the main problem appears to be that your code is still displaying signs of its no strict; heritage.

    Specifically, within your compare sub, you have the loop iterator declared ahead of the first loop:

    my $k; foreach $k ( keys %{ $hrefA } ) {

    which is rarely better than declaring it in line, but is sometimes necessary if you wish to retain its value beyond the end of the loop.

    However, in the next loop, which is only entered if you found whatever you are looking for in the first, you have no loop iterator variable, but do not appear to use $_ anywhere within it?

    And the first thing you do within that second loop is test if the value of $k exists as a key within $hrefA.

    if ($result) { foreach (keys(%{$hrefB})) { ## No loop variable if ( exists $hrefA->{$k} ) { ## line 161

    But, as the value of $k is being retained from the first loop where it is explicitly looping over the keys of $hrefA (as shown above), then that test would appear to be redundant.

    Except, the undefined warning for line 161 relates to the fact that somehow you are reaching this line with $k undefined.

    My best guess, without having tried to work my way through and understand everything you are doing, is that your second loop is meant to be iterating the keys of $hrefB and checking whether they exist in $hrefA, and that if you replaced those two snippets with;

    ## my $k; foreach my $k ( keys %{ $hrefA } ) { ... if ($result) { foreach my $k (keys(%{$hrefB})) { ## No loop variable if ( exists $hrefA->{$k} ) { ## line 161

    That might eliminate a large number of the warnings, and might also get you closer to making your code do what you are expecting.

    Until you eliminate all the warnings your code is producing, it is impossible (or at least, asking a lot) for us to see symptoms of the problem you describe and so attempt to help you solve that problem.

    I don't insist on use strict; use warnings; although I always use them myself, but in this case, it is very obvious that your code is not doing what you think it is doing, and if you had them enabled, you would have realised it yourself.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      Perhaps I'm more of an Initiate (not even a Novice) than I thought. Am I not using strict in all examples (even the admittedly horrible original post) above? In Re^4: multi-threaded win32::console it behaves (produces results without errors or warnings) as I expect. The problem is only when I introduce Win32::console.

      Which of the above versions of the code produced the line numbers in your warnings / errors?

      Is there a platform or Perl version difference that is causing the problems? I'm on:

      C:\chas_sandbox>perl -version This is perl, v5.8.8 built for MSWin32-x86-multi-thread (with 33 registered patches, see perl -V for more detail) Copyright 1987-2006, Larry Wall Binary build 819 [267479] provided by ActiveState http://www.ActiveSta +te.com Built Aug 29 2006 12:42:41 Perl may be copied only under the terms of either the Artistic License + or the GNU General Public License, which may be found in the Perl 5 source ki +t. Complete documentation for Perl, including FAQ lists, should be found +on this system using "man perl" or "perldoc perl". If you have access to + the Internet, point your browser at http://www.perl.org/, the Perl Home Pa +ge. C:\chas_sandbox>ver Microsoft Windows XP [Version 5.1.2600] C:\chas_sandbox>

        Am I not using strict in all examples (even the admittedly horrible original post) above?

        You are putting it in the code you post, but you obviously aren't using it when you run the code on your machine, else you would be seeing and correcting these errors and warnings--not me.

        Trust me when I say that the versions of Perl and XP will not be causing the warnings I reported in the code from the post to which I replied.

        When I correct the logic errors I detailed, then I get this output:

        C:\test>632320 same values, different keys: A->b same values, different keys: A->h same values, different keys: A->s same values, different keys: A->t same values, different keys: A->d same values, different keys: A->f same values, different keys: A->h same values, different keys: A->w same values, different keys: A->c same values, different keys: A->d same values, different keys: A->f same values, different keys: A->p same values, different keys: A->c same values, different keys: A->g same values, different keys: A->o same values, different keys: A->v same values, different keys: A->o same values, different keys: A->g same values, different keys: A->j same values, different keys: A->n 1/177 .

        And if I comment out the print line producing that "same values, ..." output I get

        C:\test>632320 1/177 . 11/0

        That's obviously only the final state when the program ends. I see the values changing as the program progresses. And that I think is the answer to your original question. Mixing consoleIO and prints means that the latter is overlaying the former and causing the screen to scroll.

        You should opt for using one or the other. Or, clear the screen first, write your status information to the top line whilst saving and restoring the current cursor postion before and after each write, and then your print statements can scroll down the screen and the status information stays fixed.

        Personally, I think that avoiding mixing them is the easiest option.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re^3: multi-threaded win32::console
by goibhniu (Hermit) on Aug 14, 2007 at 00:26 UTC

    Second variant of my full code. I've gotten rid of all my $threadCONSOLE refernces and am just using my $CONSOLE from main.

    I get the same results (give or take the non-determinacy in mutliple threads running slightly differently each time).

    C:\chas_sandbox>test.pl 1/177 . 3/8 4/4 4/20 C:\chas_sandbox>

      Third Variant of my full code. There is no Win32::Console; just normal print statements.

      with the following output:

      (this time the output gets readmore tags)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://632345]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2024-04-25 19:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found