Программирование на языке Ruby
Программирование на языке Ruby читать книгу онлайн
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
Записать результат в x Записать результат в xЯсно, что каждый поток увеличивает на 1 то значение, которое видит. Но не менее ясно и то, что после увеличения на 1 обоими потоками
xИ это лишь самая простая из проблем, возникающих в связи с синхронизацией. Для решения более сложных приходится прилагать серьезные усилия — это предмет изучения специалистами в области теоретической информатики и математики.
13.2.1. Синхронизация с помощью критических секций
Простейший способ синхронизации дают критические секции. Когда поток входит в критическую секцию программы, гарантируется, что никакой другой поток не войдет в нее, пока первый не выйдет.
Если акцессору
Thread.criticaltruecriticalx = 0t1 = Thread.new do 1.upto(1000) do Thread.critical = true x = x + 1 Thread.critical = false endendt2 = Thread.new do 1.upto(1000) do Thread.critical = true x = x + 1 Thread.critical = false endendt1.joint2.joinputs xТеперь последовательность выполнения изменилась; взгляните, в каком порядке работают потоки
t1t2t1 t2----------------------------- -----------------------------Прочитать значение x (123)Увеличить значение на 1 (124)Записать результат в x Прочитать значение x (124) Увеличить значение на 1 (125) Записать результат в xВозможны такие комбинации операций с потоками, при которых поток планируется даже тогда, когда какой-то другой поток находится в критической секции.
Простейший случай — вновь созданный поток начинает исполнение немедленно вне зависимости от того, занимает какой-то другой поток критическую секцию или нет. Поэтому описанную технику лучше применять только в самых простых ситуациях.
13.2.2. Синхронизация доступа к ресурсам (mutex.rb)
В качестве примера рассмотрим задачу индексирования Web-сайтов. Мы извлекаем слова из многочисленных страниц в Сети и сохраняем их в хэше. Ключом является само слово, а значением — строка, идентифицирующая документ и номер строки в этом документе.
Постановка задачи и так достаточно груба. Но мы огрубим ее еще больше, введя следующие упрощающие допущения:
• будем представлять удаленные документы в виде строк;
• ограничимся всего тремя строками (они будут «зашиты» в код);
• сетевые задержки будем моделировать «засыпанием» на случайный промежуток времени.
Взгляните на программу в листинге 13.1. Она даже не печатает получаемые данные целиком, а выводит лишь счетчик слов (не уникальный). Каждый раз при чтении или обновлении хэша мы вызываем метод
hesitate@list = []@list[0]="shoes shipsnsealing-wax"@list[1]="cabbages kings"@list[2]="quarksnshipsncabbages"def hesitate sleep rand(0)end@hash = {}def process_list(listnum) lnum = 0 @list[listnum].each do |line| words = line.chomp.split words.each do |w| hesitate if @hash[w] hesitate @hash[w] += ["#{listnum}:#{lnum}"] else hesitate @hash[w] = ["#{listnum}:#{lnum}"] end end lnum += 1 endendt1 = Thread.new(0) {|num| process_list(num) }t2 = Thread.new(1) {|num| process_list(num) }t3 = Thread.new(2) {|num| process_list(num) }t1.joint2.joint3.joincount = 0@hash.values.each {|v| count += v.size }puts "Всего слов: #{count} " # Может быть напечатано 7 или 8!Здесь имеется проблема. Если ваша система ведет себя примерно так же, как наша, то программа может напечатать одно из двух значений! В наших тестах с одинаковой вероятностью печаталось 7 или 8. Если слов и списков больше, то и разброс окажется более широким.
Попробуем исправить положение с помощью мьютекса, который будет контролировать доступ к разделяемому ресурсу. (Слово «mutex» — это сокращение от mutual exclusion, «взаимная блокировка».)
