Threads, thread unsafe modules, and an alternative

E. Choroba

Prague.pm

GoodData

PerlMonks, CPAN, StackOverflow

choroba@matfyz.cz

GUI client for the PerlMonks Chatter Box

Why?

GUI client for the PerlMonks Chatter Box (2)

Modules

Features

PM::CB::G

PerlMonks::ChatterBox::GUI

Three components:

threads

#! /usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

use threads;

my $t = 'threads'->create(
    sub { say 'Inside ', 'threads'->tid }
);
say $t->tid, ' created';
sleep 1;
say 'About to finish';
$t->join;
say 'Done';
Inside 1
1 created
About to finish
Done

threads::shared

#! /usr/bin/perl
use warnings;
use strict;
use threads;
use threads::shared;

use constant GOAL => 5_000_000;

my $x :shared = 0;

my $t = 'threads'->create(sub {
    ++$x for 1 .. GOAL;
    return 'ok'
});

sleep print $x, "\n" while $x < GOAL;
print $t->join, "\n";
0
1951161
3904184
ok

Thread::Queue

Thread::Queue (2)

#! /usr/bin/perl
use warnings; use strict; use feature qw{ say };
use threads;
use Thread::Queue;
$| = 1;

my $q = 'Thread::Queue'->new;

my @t = map 'threads'->create(sub {
    my $tid = 'threads'->tid;
    while (defined( my $x = $q->dequeue )) {
        sleep say "$tid\t", 2 ** $x;
    }
    return "$tid done" }), 1 .. 3;

$q->enqueue($_) for 1 .. 10;
$q->end;
say $_->join for @t;
2	2
3	4
1	8
2	16
3	32
1	64
2	128
3	256
1	512
2	1024
1 done
2 done
3 done

Thread Safety

Thread unsafe modules

Thread Safety (2)

Example

#! /usr/bin/perl
use warnings;
use strict;

use threads;
use Tk;

my $t = 'threads'->create(sub {

    my $mw = 'MainWindow'->new;
    MainLoop();
});
$t->join;

Thread Safety (3)

Example

Unbalanced string table refcount: (1) for "wrap" during global destruction.
Unbalanced string table refcount: (1) for "HighlightThickness" during global destruction.
Unbalanced string table refcount: (1) for "shapeStyle" during global destruction.
Unbalanced string table refcount: (1) for "Cut" during global destruction.
Unbalanced string table refcount: (1) for "beNiceToColormap" during global destruction.
Unbalanced string table refcount: (1) for "#augment \n ~Shift ~Meta ~Alt &lt;Key>osfDelete: delete-next-character() \n ~Shift ~Meta ~Alt &lt;Key>osfBackSpace: delete-previous-character() \n" during global destruction.
Unbalanced string table refcount: (1) for "lpr" during global destruction.
Unbalanced string table refcount: (1) for "Relief" during global destruction.
Unbalanced string table refcount: (1) for "Background" during global destruction.
Unbalanced string table refcount: (1) for "grey67" during global destruction.
Unbalanced string table refcount: (1) for "highlightColor" during global destruction.
Unbalanced string table refcount: (1) for "Font" during global destruction.
Unbalanced string table refcount: (1) for "Visual" during global destruction.
Unbalanced string table refcount: (1) for "printCommand" during global destruction.
Unbalanced string table refcount: (1) for "visual" during global destruction.
Unbalanced string table refcount: (1) for "activeBackground" during global destruction.
Unbalanced string table refcount: (1) for "customization" during global destruction.
Unbalanced string table refcount: (1) for "XmTextField" during global destruction.
Unbalanced string table refcount: (1) for "black" during global destruction.
Unbalanced string table refcount: (1) for "numeric" during global destruction.
Unbalanced string table refcount: (1) for "white" during global destruction.
Unbalanced string table refcount: (1) for "pointerColorBackground" during global destruction.
Unbalanced string table refcount: (1) for "foreground" during global destruction.
Unbalanced string table refcount: (1) for "Copy" during global destruction.
Unbalanced string table refcount: (1) for "highlightThickness" during global destruction.
Unbalanced string table refcount: (1) for "Dialog" during global destruction.
Unbalanced string table refcount: (1) for "ScrollbarBackground" during global destruction.
Unbalanced string table refcount: (1) for "padX" during global destruction.
Unbalanced string table refcount: (1) for "Use" during global destruction.
Unbalanced string table refcount: (1) for "SimpleMenu" during global destruction.
Unbalanced string table refcount: (1) for "ttk" during global destruction.
Unbalanced string table refcount: (1) for "False" during global destruction.
Unbalanced string table refcount: (1) for "Ghostview" during global destruction.
Unbalanced string table refcount: (1) for "TearOff" during global destruction.
Unbalanced string table refcount: (1) for "compound" during global destruction.
Unbalanced string table refcount: (1) for "SmeBSB" during global destruction.
Unbalanced string table refcount: (1) for "Screen" during global destruction.
Unbalanced string table refcount: (1) for "type" during global destruction.
Unbalanced string table refcount: (1) for "LeftTab" during global destruction.
Unbalanced string table refcount: (1) for "HighlightBackground" during global destruction.
Unbalanced string table refcount: (1) for "Command" during global destruction.
Unbalanced string table refcount: (1) for "geometry" during global destruction.
Unbalanced string table refcount: (1) for "TransientShell" during global destruction.
Unbalanced string table refcount: (1) for "XmText" during global destruction.
Unbalanced string table refcount: (1) for "height" during global destruction.
Unbalanced string table refcount: (1) for "cursor" during global destruction.
Unbalanced string table refcount: (1) for "Type" during global destruction.
Unbalanced string table refcount: (1) for "scrollHorizontal" during global destruction.
Unbalanced string table refcount: (1) for "background" during global destruction.
Unbalanced string table refcount: (1) for "title" during global destruction.
Unbalanced string table refcount: (1) for "tearOffCommand" during global destruction.
Unbalanced string table refcount: (1) for "shadowWidth" during global destruction.
Unbalanced string table refcount: (1) for "takeFocus" during global destruction.
Unbalanced string table refcount: (1) for "Compound" during global destruction.
Unbalanced string table refcount: (1) for "AxeText" during global destruction.
Unbalanced string table refcount: (1) for "Ttk" during global destruction.
Unbalanced string table refcount: (1) for "grey37" during global destruction.
Unbalanced string table refcount: (1) for "45" during global destruction.
Unbalanced string table refcount: (1) for "Geometry" during global destruction.
Unbalanced string table refcount: (1) for "Label" during global destruction.
Unbalanced string table refcount: (1) for "displayLang" during global destruction.
Unbalanced string table refcount: (1) for "-color" during global destruction.
Unbalanced string table refcount: (1) for "XConsole" during global destruction.
Unbalanced string table refcount: (1) for "line" during global destruction.
Unbalanced string table refcount: (1) for "Toggle" during global destruction.
Unbalanced string table refcount: (1) for "topShadowContrast" during global destruction.
Unbalanced string table refcount: (1) for "BorderWidth" during global destruction.
Unbalanced string table refcount: (1) for "false" during global destruction.
Unbalanced string table refcount: (1) for "Undo" during global destruction.
Unbalanced string table refcount: (1) for "font" during global destruction.
Unbalanced string table refcount: (1) for "20" during global destruction.
Unbalanced string table refcount: (1) for "screen" during global destruction.
Unbalanced string table refcount: (1) for "highlightBackground" during global destruction.
Unbalanced string table refcount: (1) for "Foreground" during global destruction.
Unbalanced string table refcount: (1) for "padY" during global destruction.
Unbalanced string table refcount: (1) for "relief" during global destruction.
Unbalanced string table refcount: (1) for "2" during global destruction.
Unbalanced string table refcount: (1) for "pushThumb" during global destruction.
Unbalanced string table refcount: (1) for "selectColor" during global destruction.
Unbalanced string table refcount: (1) for "Paste" during global destruction.
Unbalanced string table refcount: (1) for "shadowThickness" during global destruction.
Unbalanced string table refcount: (1) for "borderWidth" during global destruction.
Unbalanced string table refcount: (1) for "text" during global destruction.
Unbalanced string table refcount: (1) for "PushThumb" during global destruction.
Unbalanced string table refcount: (1) for "#override \n ~Shift ~Meta ~Alt &lt;Key>Delete: delete-next-character() \n ~Shift ~Meta ~Alt &lt;Key>osfDelete: delete-next-character() \n ~Shift ~Meta ~Alt &lt;Key>osfBackSpace: delete-previous-character() \n" during global destruction.
Unbalanced string table refcount: (1) for "activeForeground" during global destruction.
Unbalanced string table refcount: (1) for "Translations" during global destruction.
Unbalanced string table refcount: (1) for "Rectangle" during global destruction.
Unbalanced string table refcount: (1) for "timeFormat" during global destruction.
Unbalanced string table refcount: (1) for "use" during global destruction.
Unbalanced string table refcount: (1) for "Pad" during global destruction.
Unbalanced string table refcount: (1) for "activeBorderWidth" during global destruction.
Unbalanced string table refcount: (1) for "Scrollbar" during global destruction.
Unbalanced string table refcount: (1) for "XSysinfo" during global destruction.
Unbalanced string table refcount: (1) for "#override \n ~Shift ~Meta ~Alt &lt;Key>Delete: delete-next-character() \n" during global destruction.
Unbalanced string table refcount: (1) for "Xcursor" during global destruction.
Unbalanced string table refcount: (1) for "Cursor" during global destruction.
Unbalanced string table refcount: (1) for "MenuButton" during global destruction.
Unbalanced string table refcount: (1) for "Panner" during global destruction.
Unbalanced string table refcount: (1) for "Container" during global destruction.
Unbalanced string table refcount: (1) for "Menu" during global destruction.
Unbalanced string table refcount: (1) for "basicLocale" during global destruction.
Unbalanced string table refcount: (1) for "Class" during global destruction.
Unbalanced string table refcount: (1) for "Colormap" during global destruction.
Unbalanced string table refcount: (1) for "class" during global destruction.
Unbalanced string table refcount: (1) for "HighlightColor" during global destruction.
Unbalanced string table refcount: (1) for "Form" during global destruction.
Unbalanced string table refcount: (1) for "TearOffCommand" during global destruction.
Unbalanced string table refcount: (1) for "Height" during global destruction.
Unbalanced string table refcount: (1) for "Width" during global destruction.
Unbalanced string table refcount: (1) for "ScrollbarForeground" during global destruction.
Unbalanced string table refcount: (1) for "MainWindow" during global destruction.
Unbalanced string table refcount: (1) for "Mwm" during global destruction.
Unbalanced string table refcount: (1) for "inputLang" during global destruction.
Unbalanced string table refcount: (1) for "grey77" during global destruction.
Unbalanced string table refcount: (1) for "menu" during global destruction.
Unbalanced string table refcount: (1) for "Text" during global destruction.
Unbalanced string table refcount: (1) for "DisabledForeground" during global destruction.
Unbalanced string table refcount: (1) for "C" during global destruction.
Unbalanced string table refcount: (1) for "postCommand" during global destruction.
Unbalanced string table refcount: (1) for "top_left_arrow" during global destruction.
Unbalanced string table refcount: (1) for "15" during global destruction.
Unbalanced string table refcount: (1) for "tearOff" during global destruction.
Unbalanced string table refcount: (1) for "pointerColor" during global destruction.
Unbalanced string table refcount: (1) for "TakeFocus" during global destruction.
Unbalanced string table refcount: (1) for "disabledForeground" during global destruction.
Unbalanced string table refcount: (1) for "all" during global destruction.
Unbalanced string table refcount: (1) for "container" during global destruction.
Unbalanced string table refcount: (1) for "Title" during global destruction.
Unbalanced string table refcount: (1) for "width" during global destruction.
Unbalanced string table refcount: (1) for "Redo" during global destruction.
Unbalanced string table refcount: (1) for "colormap" during global destruction.
Unbalanced string table refcount: (1) for "PasteSelection" during global destruction.
Unbalanced string table refcount: (1) for "bottomShadowContrast" during global destruction.
Unbalanced string table refcount: (1) for "cursorName" during global destruction.
Attempt to free nonexistent shared string 'cursor', Perl interpreter: 0x55a2464b3260 during global destruction.
Attempt to free nonexistent shared string 'wrap', Perl interpreter: 0x55a2464b3260 during global destruction.
Attempt to free nonexistent shared string '�', Perl interpreter: 0x55a2464b3260 during global destruction.
Attempt to free nonexistent shared string '�', Perl interpreter: 0x55a2464b3260 during global destruction.
Attempt to free nonexistent shared string 'customization', Perl interpreter: 0x55a2464b3260 during global destruction.
Attempt to free nonexistent shared string 'XmTextField', Perl interpreter: 0x55a2464b3260 during global destruction.
Attempt to free nonexistent shared string 'borderWidth', Perl interpreter: 0x55a2464b3260 during global destruction.
Attempt to free nonexistent shared string 'menu', Perl interpreter: 0x55a2464b3260 during global destruction.
Attempt to free nonexistent shared string 'HighlightThickness', Perl interpreter: 0x55a2464b3260 during global destruction.
Attempt to free nonexistent shared string '�', Perl interpreter: 0x55a2464b3260 during global destruction.
Segmentation fault (core dumped)

How to combine thread unsafe modules with threads

Thread Safety (4)

#! /usr/bin/perl
use warnings;
use strict;

use threads;
use Tk;

my $t = 'threads'->create(sub {

    my $mw = 'MainWindow'->new;
    MainLoop();
});
$t->join;

Thread Safety (4)

#! /usr/bin/perl
use warnings;
use strict;

use threads;


my $t = 'threads'->create(sub {
    require Tk;
    my $mw = 'MainWindow'->new;
    MainLoop();
});
$t->join;

Thread Safety (4)

#! /usr/bin/perl
use warnings;
use strict;

use threads;


my $t = 'threads'->create(sub {
    require Tk;
    my $mw = 'MainWindow'->new;
    Tk::MainLoop();
});
$t->join;

Perl Threads Are Not X Threads

perlthrtut

Perl Threads Are Not X Threads for all values of X. They aren't POSIX threads, or DecThreads, or Java's Green threads, or Win32 threads.

Perl Threads Are Not X Threads

Threads

Threads

Officially discouraged

WARNING

The "interpreter-based threads" provided by Perl are not the fast, lightweight system for multitasking that one might expect or hope for. Threads are implemented in a way that make them easy to misuse. Few people know how to use them correctly or will be able to provide help.

The use of interpreter-based threads in perl is officially discouraged.

Discouraged

Threads

perldoc perlpolicy

discouraged

From time to time, we may mark language constructs and features which we consider to have been mistakes as discouraged. Discouraged features aren't currently candidates for removal, but we may later deprecate them if they're found to stand in the way of a significant improvement to the Perl core.

But see Bug #125106 for perl5: Why are threads discouraged?

Alternative to threads

MCE

Two possible implementations

Threads to MCE

When porting PM::CB::G from threads to MCE, I only needed to change

my ($to_gui, $to_comm, $to_control)
    = map 'Thread::Queue'->new, 1, 2, 3;

my $control_t = 'threads'->create(sub {
    ...
});

Threads to MCE (2)

When porting PM::CB::G from threads to MCE, I only needed to change

my ($to_gui, $to_comm, $to_control)
    = map 'MCE::Channel'->new, 1, 2, 3;

my $control_t = 'MCE::Child'->create(sub {
    ...
});

Threads to MCE (3)

When porting PM::CB::G from threads to MCE, I only needed to change

my ($to_gui, $to_comm, $to_control)
    = map 'MCE::Shared'->queue, 1, 2, 3;

my $control_t = 'MCE::Hobo'->create(sub {
    ...
});

Threads to MCE (4)

When porting PM::CB::G from threads to MCE, I only needed to change

my ($to_gui, $to_comm, $to_control)
    = map $queue_class->$queue_constructor, 1, 2, 3;

my $control_t = $worker_class->create(sub {
    ...
});

MCE

Conclusion

Thank you.

Questions?

https://e-choroba.eu/19-pmcbg/