Coro で Read-Write Lock パターン
増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編 を参考に Coro で Read-Write Lock パターンを実装。
Read-Write Lockは、
- スレッドが読んている間は、別のスレッドも読める
- スレッドが書いている間は、別のスレッドは読めない
- スレッドが読んている間は、別のスレッドは書けない
- スレッドが書いている間は、別のスレッドは書けない
#!/usr/bin/perl use strict; use warnings; package Reader; use Coro; sub new { my ( $class, %args ) = @_; my %defaults = ( name => 'Reader' ); %args = ( %defaults, %args ); bless \%args, $class; } sub run { my $self = shift; my $coro = async { do { my $name = $self->{name}; my $board = $self->{board}; my $type = $self->{type}; print "$name : read $type data\n"; my $content = $board->read( type => $type ); print "$name : done $type data($content)\n"; }; }; return $coro; } package Writer; use Coro; sub new { my ( $class, %args ) = @_; my %defaults = ( name => 'Writer' ); %args = ( %defaults, %args ); bless \%args, $class; } sub run { my $self = shift; my $content = shift; my $coro = async { do { my $name = $self->{name}; my $board = $self->{board}; my $type = $self->{type}; print "$name : write $type data($content)\n"; $board->write( type => $type, content => $content ); print "$name : done $type data\n"; }; }; return $coro; } package Locker; use Coro; use Coro::Semaphore; use Coro::Timer; sub new { my ( $class, %args ) = @_; $args{reading_readers} = 0; $args{waiting_writers} = 0; $args{writing_writers} = 0; $args{prefer_writer} = 1; $args{signal} = Coro::Signal->new; bless \%args, $class; } sub read_lock { my $self = shift; my $guard = $self->_get_guard(); while (( $self->{writing_writers} > 0 ) || ( $self->{prefer_writer} && ( $self->{waiting_writers} > 0 ) ) ) { $self->{signal}->wait(); } $self->{reading_readers}++; } sub read_unlock { my $self = shift; my $guard = $self->_get_guard(); $self->{reading_readers}--; $self->{prefer_writer} = 1; $self->{signal}->broadcast(); } sub write_lock { my $self = shift; my $guard = $self->_get_guard(); $self->{waiting_writers}++; while (( $self->{reading_readers} > 0 ) || ( $self->{writing_writers} > 0 ) ) { $self->{signal}->wait(); } $self->{waiting_writers}--; $self->{writing_writers}++; } sub write_unlock { my $self = shift; my $guard = $self->_get_guard(); $self->{writing_writers}--; $self->{prefer_writer} = 0; $self->{signal}->broadcast(); } sub _get_guard { my $self = shift; my $name = ( caller 1 )[3]; $name =~ s/.*:://; $name .= '_sem'; if ( !exists $self->{$name} ) { $self->{$name} = Coro::Semaphore->new; } else { my $class = ref $self->{$name}; if ( $class ne 'Coro::Semaphore' ) { die "$name is not Coro::Semaphore"; } } return $self->{$name}->guard(); } package Board; use Coro; use Coro::Timer; sub new { my ( $class, %args ) = @_; my %defaults = ( heavy => 'heavy data0', light => 'light data0' ); %args = ( %defaults, %args ); $args{locker} = Locker->new(); bless \%args, $class; } sub read { my $self = shift; $self->{locker}->read_lock(); my $content = $self->do_read(@_); $self->{locker}->read_unlock(); return $content; } sub do_read { my $self = shift; my %args = @_; my $type = $args{type}; if ( $type eq 'heavy' ) { Coro::Timer::sleep(1); } return $self->{$type}; } sub write { my $self = shift; $self->{locker}->write_lock(); $self->do_write(@_); $self->{locker}->write_unlock(); } sub do_write { my $self = shift; my %args = @_; my $type = $args{type}; my $content = $args{content}; if ( $type eq 'heavy' ) { Coro::Timer::sleep(1); } $self->{$type} = $content; } package main; use Coro; read_on_read(); read_on_write(); write_on_read(); write_on_write(); sub read_on_read { print "--- read_on_read\n"; my $board = Board->new(); my $reader1 = Reader->new( name => 'reader1', type => 'heavy', board => $board ); my $reader2 = Reader->new( name => 'reader2', type => 'light', board => $board ); my $reader1_coro = $reader1->run(); my $reader2_coro = $reader2->run(); $reader1_coro->join(); $reader2_coro->join(); print "\n"; } sub read_on_write { print "--- read_on_write\n"; my $board = Board->new(); my $writer = Writer->new( name => 'writer', type => 'heavy', board => $board ); my $reader = Reader->new( name => 'reader', type => 'light', board => $board ); my $writer_coro = $writer->run('heavy data1'); my $reader_coro = $reader->run(); $writer_coro->join(); $reader_coro->join(); print "\n"; } sub write_on_read { print "--- read_on_write\n"; my $board = Board->new(); my $reader = Reader->new( name => 'reader', type => 'heavy', board => $board ); my $writer = Writer->new( name => 'writer', type => 'light', board => $board ); my $reader_coro = $reader->run(); my $writer_coro = $writer->run('light data1'); $reader_coro->join(); $writer_coro->join(); print "\n"; } sub write_on_write { print "--- write_on_write\n"; my $board = Board->new(); my $writer1 = Writer->new( name => 'writer1', type => 'heavy', board => $board ); my $writer2 = Writer->new( name => 'writer2', type => 'light', board => $board ); my $writer1_coro = $writer1->run('heavy data1'); my $writer2_coro = $writer2->run('light data1'); $writer1_coro->join(); $writer2_coro->join(); print "\n"; }
出力
--- read_on_read reader1 : read heavy data reader2 : read light data reader2 : done light data(light data0) reader1 : done heavy data(heavy data0) --- read_on_write writer : write heavy data(heavy data1) reader : read light data writer : done heavy data reader : done light data(light data0) --- read_on_write reader : read heavy data writer : write light data(light data1) reader : done heavy data(heavy data0) writer : done light data --- write_on_write writer1 : write heavy data(heavy data1) writer2 : write light data(light data1) writer1 : done heavy data writer2 : done light data
参考
増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編
増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編
- 作者: 結城浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2006/03/21
- メディア: 大型本
- 購入: 15人 クリック: 287回
- この商品を含むブログ (206件) を見る