Latest web development tutorials

rubi Multithreading

Cada programa em execução no sistema é um processo. Cada processo contém um ou mais segmentos.

Um segmento é um único fluxo de controle do programa seqüencial, em simultâneo executar vários segmentos em um único programa fazer coisas diferentes, chamadas multi-threading.

Ruby, que pode ser criado por múltiplos threads classe Thread, tópicos Ruby são um peso leve, pode ser uma maneira eficiente de implementar o código paralelo.


Criando uma linha de Ruby

Para iniciar um novo tópico, você pode apenas chamar Thread.new:

# 线程 #1 代码部分
Thread.new {
  # 线程 #2 执行代码
}
# 线程 #1 执行代码

Exemplos

O exemplo a seguir mostra como usar multithreading no programa de Ruby:

#!/usr/bin/ruby

def func1
   i=0
   while i<=2
      puts "func1 at: #{Time.now}"
      sleep(2)
      i=i+1
   end
end

def func2
   j=0
   while j<=2
      puts "func2 at: #{Time.now}"
      sleep(1)
      j=j+1
   end
end

puts "Started At #{Time.now}"
t1=Thread.new{func1()}
t2=Thread.new{func2()}
t1.join
t2.join
puts "End at #{Time.now}"

O código acima é executado como resultado de:

Started At Wed May 14 08:21:54 -0700 2014
func1 at: Wed May 14 08:21:54 -0700 2014
func2 at: Wed May 14 08:21:54 -0700 2014
func2 at: Wed May 14 08:21:55 -0700 2014
func1 at: Wed May 14 08:21:56 -0700 2014
func2 at: Wed May 14 08:21:56 -0700 2014
func1 at: Wed May 14 08:21:58 -0700 2014
End at Wed May 14 08:22:00 -0700 2014

Ciclo fio da vida

1, criar um thread pode usar Thread.new, você também pode usar o mesmo Thread.start de sintaxe ou Thread.fork três maneiras de criar um fio.

2, crie uma linha sem iniciar, o segmento será executado automaticamente.

3, classe Thread define métodos para manipular discussão. Fio de blocos de código de execução Thread.new.

4, o bloco da linha é o valor da última declaração no segmento, o segmento pode ser invocado por um método, se o fio for concluída, ele retorna o valor da linha, ou não retorna um valor até que o segmento está terminada.

5, método Thread.current retorna um objeto para a representação atual segmento. Método Thread.main retorna o thread principal.

6, o método é realizado por fios Thread.Join, este método irá suspender o thread principal até que o segmento atual está terminado.


Estado rosca

Thread tem cinco estados:

Estado rosca Valor de retorno
executável corrida
adormecido adormecido
desistir abortando
terminação normal falso
Finalização anormal ocorre zero

Fios e aberrante

Quando uma exceção de thread ocorre e nenhum resgate foi capturado, o segmento normalmente seria encerrado sem aviso. No entanto, se outros tópicos Tópico # juntar-se porque a relação tem sido esperando por esta discussão, em seguida, os segmentos de espera também será levantada a mesma exceção.

begin
  t = Thread.new do
    Thread.pass    # 主线程确实在等join
    raise "unhandled exception"
  end
  t.join
rescue
  p $!  # => "unhandled exception"
end

Use as seguintes três métodos, você pode obter o intérprete para interromper a operação quando um segmento é encerrado devido a uma exceção.

  • script de inicialização especifica opção-d, e operação em modo de depuração.
  • Com Thread.abort_on_exception definir o sinalizador.
  • Use Thread#abort_on_exception bandeira especificado set discussão.

Quando se utiliza um de três métodos descritos acima, todo o intérprete irá ser interrompido.

t = Thread.new { ... }
t.abort_on_exception = true

Sincronização Tópico

Em Ruby, fornece três maneira sincronizada, a saber:

1. Mutex classe implementa sincronização de thread

2. classe Fila de transferência de dados Regulatory implementar a sincronização de threads

controle de sincronização 3. Use ConditionVariable

Por Mutex classe implementa sincronização de thread

Por classe Mutex implementa o controle de sincronização de threads, se você também precisa de um relógio variável de programa em vários segmentos, você pode usar o bloqueio para bloquear a parte variável. Código é a seguinte:

#!/usr/bin/ruby

require "thread"
puts "Synchronize Thread"

@num=200
@mutex=Mutex.new

def buyTicket(num)
     @mutex.lock
          if @num>=num
               @num=@num-num
               puts "you have successfully bought #{num} tickets"
          else
               puts "sorry,no enough tickets"
          end
     @mutex.unlock
end

ticket1=Thread.new 10 do
     10.times do |value|
     ticketNum=15
     buyTicket(ticketNum)
     sleep 0.01
     end
end

ticket2=Thread.new 10 do
     10.times do |value|
     ticketNum=20
     buyTicket(ticketNum)
     sleep 0.01
     end
end

sleep 1
ticket1.join
ticket2.join

O código acima é executado como resultado de:

Synchronize Thread
you have successfully bought 15 tickets
you have successfully bought 20 tickets
you have successfully bought 15 tickets
you have successfully bought 20 tickets
you have successfully bought 15 tickets
you have successfully bought 20 tickets
you have successfully bought 15 tickets
you have successfully bought 20 tickets
you have successfully bought 15 tickets
you have successfully bought 20 tickets
you have successfully bought 15 tickets
sorry,no enough tickets
sorry,no enough tickets
sorry,no enough tickets
sorry,no enough tickets
sorry,no enough tickets
sorry,no enough tickets
sorry,no enough tickets
sorry,no enough tickets
sorry,no enough tickets

Além de usar locked variável, você também pode usar try_lock variável bloqueada, você também pode usar Mutex.synchronize sincronizar o acesso a uma variável particular.

a transferência de dados de regulamentação da classe Queue implementa sincronização de thread

Fila de classe que representa um segmento fila de suporte, a fila pode ser sincronizado com o fim da visita. Diferentes segmentos podem usar uma classe unificada, mas não se preocupe se os dados podem ser sincronizados nesta fila, além de limitar o uso de SizedQueue comprimento da fila de classe

classe SizedQueue pode ser muito conveniente para nos ajudar a desenvolver aplicações com rosca para sincronizar, problemas de sincronização deve ser adicionado à longa fila, você não se preocupam com threads.

produtores e consumidores clássicos:

#!/usr/bin/ruby

require "thread"
puts "SizedQuee Test"

queue = Queue.new

producer = Thread.new do
     10.times do |i|
          sleep rand(i) # 让线程睡眠一段时间
          queue << i
          puts "#{i} produced"
     end
end

consumer = Thread.new do
     10.times do |i|
          value = queue.pop
          sleep rand(i/2)
          puts "consumed #{value}"
     end
end

consumer.join

Programa de saída:

SizedQuee Test
0 produced
1 produced
consumed 0
2 produced
consumed 1
consumed 2
3 produced
consumed 34 produced

consumed 4
5 produced
consumed 5
6 produced
consumed 6
7 produced
consumed 7
8 produced
9 produced
consumed 8
consumed 9

Variáveis ​​Thread

Thread pode ter sua variável privada, uma variável privada de discussão escrita segmento quando o segmento é criado. Ele pode ser usado dentro do âmbito da rosca, mas o fio não pode ser partilhada externamente.

Mas, às vezes, as variáveis ​​locais de rosca não precisa de outro segmento ou o segmento principal para acessar como fazer? ruby lhes permite criar uma lista de discussão é fornecido pelo nome da variável, o segmento é visto como estilo semelhante tabela hash hash. Por [] = escrito por [] ler os dados. Nós olhamos para o seguinte código:

#!/usr/bin/ruby

count = 0
arr = []

10.times do |i|
   arr[i] = Thread.new {
      sleep(rand(0)/10.0)
      Thread.current["mycount"] = count
      count += 1
   }
end

arr.each {|t| t.join; print t["mycount"], ", " }
puts "count = #{count}"

O código acima é executado saída é:

8, 0, 3, 7, 2, 1, 6, 5, 4, 9, count = 10

Os principais thread espera para a execução segmento de criança é concluída, e em seguida, gera cada valor. .


a prioridade do thread

A prioridade da linha é o principal fator que afeta o agendamento de segmento. Outros factores incluem o comprimento de tempo para a realização da CPU fios pacote de programação e assim por diante.

Você pode usar Thread.priority obtido prioridade de um segmento e usar Thread.priority = método para ajustar a prioridade do thread.

defaults prioridade de um segmento para 0. execução mais rápida de maior prioridade.

Um thread pode acessar todos os dados no âmbito da sua própria, mas se houver outros tópicos precisam acessar dados em um segmento deve ser a forma de fazê-lo? classe Thread fornece dados de segmento acessar um ao outro, você pode simplesmente colocar um fio como uma tabela hash, pode ser usado em qualquer segmento [] data = escrita, utilize [] ler os dados.

athr = Thread.new { Thread.current["name"] = "Thread A"; Thread.stop }
bthr = Thread.new { Thread.current["name"] = "Thread B"; Thread.stop }
cthr = Thread.new { Thread.current["name"] = "Thread C"; Thread.stop }
Thread.list.each {|x| puts "#{x.inspect}: #{x["name"]}" }

Podemos ver, o segmento como uma tabela hash, utilize [] e [] = método, conseguimos partilha de dados entre threads.


mutex rosca

Mutex (Exclusão Mutal = mutex) é um método de programação multi-threaded, um mecanismo para impedir que duas threads simultaneamente para os mesmos recursos públicos (como variáveis ​​globais) ler e escrever.

Exemplos de não utilização Mutax

#!/usr/bin/ruby
require 'thread'

count1 = count2 = 0
difference = 0
counter = Thread.new do
   loop do
      count1 += 1
      count2 += 1
   end
end
spy = Thread.new do
   loop do
      difference += (count1 - count2).abs
   end
end
sleep 1
puts "count1 :  #{count1}"
puts "count2 :  #{count2}"
puts "difference : #{difference}"

Execute o exemplo acima saída é:

count1 :  9712487
count2 :  12501239
difference : 0

Exemplo de Mutax

#!/usr/bin/ruby
require 'thread'
mutex = Mutex.new

count1 = count2 = 0
difference = 0
counter = Thread.new do
   loop do
      mutex.synchronize do
         count1 += 1
         count2 += 1
      end
    end
end
spy = Thread.new do
   loop do
       mutex.synchronize do
          difference += (count1 - count2).abs
       end
   end
end
sleep 1
mutex.lock
puts "count1 :  #{count1}"
puts "count2 :  #{count2}"
puts "difference : #{difference}"

Execute o exemplo acima saída é:

count1 :  1336406
count2 :  1336406
difference : 0

impasse

Mais de duas unidades de operação, ambos os lados estão esperando que o outro para parar de correr, para obter os recursos do sistema, mas não uma saída precoce partido, esta situação é chamado de impasse.

Por exemplo, um p1 processo leva up display, enquanto você deve usar a impressora e a impressora está ocupada pelo processo P2, P2 deve também usar o monitor, formando assim um impasse.

Quando usamos Mutex objeto precisa de atenção de impasse thread.

Exemplos

#!/usr/bin/ruby
require 'thread'
mutex = Mutex.new

cv = ConditionVariable.new
a = Thread.new {
   mutex.synchronize {
      puts "A: I have critical section, but will wait for cv"
      cv.wait(mutex)
      puts "A: I have critical section again! I rule!"
   }
}

puts "(Later, back at the ranch...)"

b = Thread.new {
   mutex.synchronize {
      puts "B: Now I am critical, but am done with cv"
      cv.signal
      puts "B: I am still critical, finishing up"
   }
}
a.join
b.join

O exemplo acima saída é:

A: I have critical section, but will wait for cv
(Later, back at the ranch...)
B: Now I am critical, but am done with cv
B: I am still critical, finishing up
A: I have critical section again! I rule!

método de classe Thread

Tópico (thread) método de classe completa da seguinte forma:

Não. método Descrição
1 Thread.abort_on_exception
Se é verdade, uma vez que um segmento termina devido a uma exceção, todo o intérprete será interrompido. O valor padrão é falso, isto é, em circunstâncias normais, se uma exceção de thread ocorre e a exceção não é thread # juntar e outra detectado, o fio será encerrada sem aviso.
2 Thread.abort_on_exception =
Se definido comoverdadeiro,uma vez que um segmento termina devido a uma exceção, todo o intérprete será interrompido. Retorna novo estado
3 Thread.critical
Retorna um valor booleano.
4 Thread.critical =
Quando o valor for true, o segmento não será alterado. Se o segmento atual para pendurar (parar) ou sinal de intervenção (sinal), o seu valor será automaticamente alterado para falso.
5 Thread.current
Retorna o segmento em execução atual (o segmento atual).
6 Thread.exit
Ele termina o segmento atual. Retorna o atual segmento. Se o segmento atual é o único fio, usando a saída (0) para encerrar o seu funcionamento.
7 Thread.fork {block}
Como com Thread.new gerar threads.
8 Thread.kill (aThread)
Encerrar o segmento em execução.
9 Thread.list
Retorna uma matriz de rosca ao vivo está em execução ou estado suspenso.
10 Thread.main
Voltar para o segmento principal.
11 Thread.new ([arg] *) { | args | block}
Gerar fio e começar a execução. O número será transmitida intacta para o bloco. Isto pode iniciar um segmento, ao mesmo tempo, o valor será passado para as variáveis ​​locais inerentes ao fio.
12 Thread.pass
O direito de executar outros tópicos. Isso não muda o estado das threads em execução, mas vai entregar o controle de outros segmentos podem ser executados (programação explícita thread).
13 Thread.start ([args] *) { | args | block}
Gerar fio e começar a execução. O número será transmitida intacta para o bloco. Isto pode iniciar um segmento, ao mesmo tempo, o valor será passado para as variáveis ​​locais inerentes ao fio.
14 Thread.stop
O thread atual é suspenso até que os outros segmentos método run novamente acorda o segmento.

método de instância de discussão

O exemplo a seguir chama o método de instância de discussão juntar-se:

#!/usr/bin/ruby

thr = Thread.new do   # 实例化
   puts "In second thread"
   raise "Raise exception"
end
thr.join   # 调用实例化方法 join

Aqui está uma lista completa de exemplos do método:

Não. método Descrição
1 thr [nome]
Remova a linha do nome correspondente aos dados inerentes. nome pode ser uma string ou símbolo. Se o nome não corresponder aos dados, ele retorna nulo.
2 thr [nome] =
Defina o valor do nome de rosca nos dados característicos correspondentes, o nome pode ser uma string ou símbolo. Se definido como nil, remove os dados correspondentes neste segmento.
3 thr.abort_on_exception
Retorna um valor booleano.
4 thr.abort_on_exception =
Se o valor for verdade, então uma vez por segmento é encerrado devido a uma exceção, todo o intérprete será interrompido.
5 thr.alive?
Se o segmento é "ao vivo", ele retorna true.
6 thr.exit
Encerrar o segmento em execução. Retorna self.
7 thr.join
Suspende o segmento atual até que o auto run até que o segmento termina. Se o auto devido à finalização anormal, o segmento atual irá desencadear a mesma exceção.
8 thr.key?
Se o nome correspondente aos dados inerentes tem sido tópicos definidos, em seguida, retorna true
9 thr.kill
Thread.exit similares.
10 thr.priority
Retorna a prioridade do segmento. A prioridade padrão é 0. Quanto maior o valor, maior a prioridade.
11 thr.priority =
Definir a prioridade segmento. Você também pode configurá-lo para negativo.
12 thr.raise (anException)
Dentro deste segmento forçosamente lançada.
13 thr.run
Reinicie Pendente thread (stop). A diferença é que com a excitação vai realizar segmento de comutação imediatamente. Se usar este método para processar os mortos serão levantados exceção ThreadError.
14 thr.safe_level
Retorna nível de auto segurança. Safe_level a corrente fio $ SAFE mesma.
15 thr.status
Usando a cadeia "run", "sono" ou "abortar" para indicar o estado do fio vivo se um segmento é encerrado normalmente, então ele retorna false. Ruoyin finalização anormal, em seguida, retorna nil.
16 thr.stop?
Se o segmento é encerrado Estado (morto) ou suspender (stop), o retorno verdadeiro.
17 thr.value
Espere até que o segmento de auto termina (equivalente a juntar-se), devolver o valor do bloco do fio se as threads em execução ocorrer durante anormal, a exceção será levantada novamente.
18 thr.wakeup
O estado é suspenso (parar) de fio para o estado de pronto (pista), se o método é realizado no segmento de mortos vai aumentar ThreadError exceção.