2015-12-14 2 views
1

Я работаю над скриптом обработки многопроцессорных файлов. После опроса нитей/форкинга я узнал о IPC (pipe/socket) и, наконец, DRb. Кажется, что это самый способный из всех вариантов и относительно удобный.Ruby DRb - Thread-Safety

Я читал в о безопасности потока на: https://en.wikibooks.org/wiki/Ruby_Programming/Standard_Library/DRb

Но когда я попытался их примеры не похоже, чтобы получить потокобезопасной результат.

Потокобезопасная сервер:

require 'drb' 
require 'thread' 

class MyStore 
    def initialize 
    @hash = { :counter=>0 } 
    @mutex = Mutex.new 
    end 
    def inc(elem) 
    @mutex.synchronize do 
     self[elem] = self[elem].succ 
    end 
    end 
    def [](elem) 
    @hash[elem] 
    end 
    def []=(elem,value) 
    @hash[elem] = value 
    end 
end 

mystore = MyStore.new 
DRb.start_service('druby://localhost:9000', mystore) 
DRb.thread.join 

Клиент:

require 'drb'  
obj = DRbObject.new(nil, 'druby://localhost:9000') 
STDOUT.sync = true 

100.times do 
    puts obj[:counter] 
    obj.inc(:counter) 
    obj[:lastaccess] = Time.now 
end 

Я бегу код сервера первой в фоновом режиме. позже я запускаю код клиента дважды:

ruby client.rb > 1.txt & ; ruby client.rb > 2.txt 

Теперь я ожидал увидеть различные числа в файлах 1.txt и 2.txt, поскольку каждый клиент берет на себя управление счетчиком и не освобождает его, пока он выполнил приращение.

Какая очевидная проблема мне не хватает? :)

ответ

1

Проблема в вашей петле. Метод inc сервера является потокобезопасным. Однако ваш доступ к obj[:counter] не является потокобезопасным. Таким образом, вы заметите, что при запуске вашего примера он выполняет 200 общих приращений (100 для каждого процесса), потому что вы можете видеть, что последний напечатанный номер равен 199. Это означает, что запросы inc должным образом находятся в очереди и выполняются индивидуально.

Причина, по которой вы на самом деле не видите все 200 номеров (0-199), индивидуально напечатанных (т. Е. Вы видите несколько повторяющихся номеров в двух файлах), потому что ваш цикл просто выполняет puts obj[:counter], как только он попадает в эту строку код. Текущее значение obj[:counter] печатается независимо от состояния мьютекса, потому что вы не проверяете, заблокирован ли он в настоящий момент. Это означает, что каждый файл печатает 100 полных чисел между 0-199, но они не гарантируются друг от друга между двумя файлами.

Чтобы сделать ваш пример работы, вы хотели бы сделать печать внутри кода, который заблокирован мьютексом, а затем добавить дополнительный аргумент функции, чтобы вы могли проверить, с какого процесса клиент он пришел. Или, на мой взгляд, вы уже доказали, что это сработало, потому что приращение происходит 200 раз.

0

Ответ Майка замечательный, но я не могу добавить красивый комментарий в комментарий, поэтому я отвечаю на него отдельно.

изменить раздел мьютекс:

@mutex.synchronize do 
     self[elem] = self[elem].succ 
     return @hash[elem] 
    end 

И клиент:

num = obj.inc(:counter) 
    puts num 

В результате были слишком взаимоисключающие файлы.

1.TXT:

1 
3 
4 
6 
8 
10 

2.txt:

2 
5 
7 
9 
11 
13 

Последнее число равно 200, как и следовало ожидать от двух процессов, что каждое приращение в 100 раз.

+0

Если мой ответ привел к разрешению, вы могли бы принять его :). Ваш хороший пример того, как вы можете изменить код, чтобы он был потокобезопасным, но я считаю, что мое - более общее объяснение проблемы. –

+0

Все еще знакомы с пользовательским интерфейсом. Принял ваш ответ :) –

Смежные вопросы