持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情
一、线程的创建
线程就是能在一个程序中处理若干控制流的功能,Ruby 中线程是用户级线程并且依赖操作系统,线程是进程中的一个实体,是被系统独立调度和分发的基本单位,线程可与同属一个进程的其他线程共享进程的全部资源,但是线程并不用于系统资源。
线程可以将占据长时间的任务放到后台处理,可以加快程序的运行速度,例如在一些等待的任务上实现用户输入、文件读写以及网络收发数据等,线程也可以释放一些珍贵的资源如内存等。
Ruby 中创建线程只需要实例化 Thread 类即可,同时为线程添加一个作为线程体的代码块。
thread = Thread.new do
# 线程块代码
end
多个线程可以并发执行
i = 1
t1 = Thread.new 6 do |val|
while i < val
puts i
i = i + 1
end
end
t2 = Thread.new do
5.times do |a|
puts "第 #{a + 1} 次输出"
end
end
t2.join
t1.join
执行上述代码,输出结果如下:
1
2
3
4
5
第 1 次输出
第 2 次输出
第 3 次输出
第 4 次输出
第 5 次输出
多个线程是交替执行的,而不是按照顺序执行的。
除了 Thead.new 可以创建线程外 Thread 的 start 方法和 fork 方法也可以创建线程
thread = Thread.start "hello" do |value|
puts "使用 start 方法创建的线程 + #{value}"
end
thread1 = Thread.fork "world" do |value|
puts "使用 fork 方法中创建的线程 + #{value}"
end
thread.join
thread1.join
执行上述代码,输出结果如下:
使用 start 方法创建的线程 + hello
使用 fork 方法中创建的线程 + world
返回并挂起当前线程
Thread 的 current 方法可以返回当前线程
mainThread = Thread.current
puts "主线程是:" + mainThread.to_s
执行上述代码,输出结果如下:
主线程是:#<Thread:0x00007fd6c205fa50 run>
join 方法可以挂起当前线程,然后执行指定的线程
i = 1
threadJoin = Thread.new 10 do |value|
while i < value
puts "#{i}"
i = i + 1
end
end
threadJoin.join
threadJoin2 = Thread.new do
10.times do |a|
puts "第 #{a} 次输出"
end
end
threadJoin2.join
执行上述代码,输出结果如下:
1
2
3
4
5
6
7
8
9
第 0 次输出
第 1 次输出
第 2 次输出
第 3 次输出
第 4 次输出
第 5 次输出
第 6 次输出
第 7 次输出
第 8 次输出
第 9 次输出
上述两个线程的执行顺序是按照从上而下执行的,而不是交替执行的,说明使用了 join 方法,当前线程会被挂起,会执行执行的线程,除了 join 方法可以挂起线程,value 方法也可以挂起线程,value 方法可以获取下城的值
显示以及停止线程
使用 pass 方法可以暂停当前线程并且执行其他线程,sleep 方法可以让线程进入休眠状态,wakeup 方法可以唤醒线程。exit 和 kill 均可以停止当前线程
puts "开始时间:" + Time.new.to_s
sleep 2
puts "暂停 2 秒后,当前时间为:" + Time.new.to_s
执行上述代码,输出结果如下:
开始时间:2022-06-17 21:08:34 +0800
暂停 2 秒后,当前时间为:2022-06-17 21:08:36 +0800
二、线程的状态
线程的状态可以通过 status 方法获取,线程的状态有以下几种:
| 线程状态 | 状态说明 |
|---|---|
| sleep | 线程处于休眠状态或者处于等待 IO |
| run | 线程处于运行状态 |
| aborting | 线程被取消 |
| false | 线程正常终止 |
| nil | 线程被非正常状态终止 |
threadPass = Thread.start do
puts "正在运行"
end
t = threadPass.status
puts "当前状态是 #{t}"
执行上述代码,输出结果如下:
当前状态是 run
Thread 类中海油几个类方法,如 main 方法指向进程的主线程。
t1 = Thread.new do
sleep 2
end
t2 = Thread.new do
if Thread.current == Thread.main
puts "当前线程是主线程"
end
end
num = Thread.list.size
puts "当前线程数量是:#{num}"
if Thread.list.include?(Thread.main)
puts "现在运行的是主线程"
end
执行上述代码,输出结果如下:
当前线程数量是:3
现在运行的是主线程
Thread 的 list 方法返回的是一个数组,用于存储线程。
同步线程
同步线程,使一组并发进程直接制约而互相发送消息从而进行互相合作、互相等待,使得各个进程按照一定的速度执行你的过程成为进程间的童虎,由于线程共享内存空间,因此使用普通变量就可以进行数据间的交换工作,但是为了使操作的时机得当,需要对线程进行同步。
Ruby 中提供了三种实现线程同步的方法,只负责同步的 Mutex 类中的方法,监管数据交接的 Queue 类中的方法,使用 Condition Variable 类实现同步。
Mutext 类
@num = 200
@mutex = Mutex.new
def ticketNum(num)
@mutex.lock
Thread.pass
if(@num > num)
@num -= num
puts "成功购买#{num}张票"
else
puts "对不起,购买的#{num}张火车票已经失败"
end
@mutex.unlock
end
ticketSuccess = Thread.new(199) {|num| ticketNum(num)}
ticketSuccess1 = Thread.new(2) {|num| ticketNum(num)}
ticketSuccess.join
ticketSuccess1.join
执行上述代码,输出结果如下:
成功购买199张票
对不起,购买的2张火车票已经失败
在上述代码中,通过 Mutex.new 创建了一个 Mutex 对象,然后使用 Mutex 的 lock 方法锁定当前进程,然后再使用 unlock 方法进行解除锁定。如果程序中有多个线程需要同时访问一个程序变脸个,可以将这变量部分使用 lock 方法锁定,才能保证不会发生错误。
Mutex 中除了使用 lock 可以将资源锁定之外,还可以使用 try_lock 方法将资源锁定,对于 lock 来说,使用 try_lock 时,如果另一个资源已经被锁定,他会返回 false。
mutex = Mutex.new
t = Thread.new do
mutex.lock
sleep 30
end
t1 = Thread.new do
if mutex.try_lock
puts "其他线程没有被占用,可以使用"
else
puts "正在使用,无法锁定"
end
end
还可以使用 synchronize 来锁定线程
@num = 200
@mutex = Mutex.new
def ticketNum(num)
@mutex.synchronize do
Thread.pass
if(@num > num)
@num -= num
puts "成功购买#{num}张票"
else
puts "对不起,购买的#{num}张火车票已经失败"
end
end
end
ticketSuccess = Thread.new(199) {|num| ticketNum(num)}
ticketSuccess1 = Thread.new(2) {|num| ticketNum(num)}
ticketSuccess.join
ticketSuccess1.join
执行上述代码,输出结果如下:
成功购买199张票
对不起,购买的2张火车票已经失败
Queue 类表示一个支持线程的队列,能够对同步队列末尾进行访问,也就是说,不同的线程可以使用同一个队列,但不用单线是否能够同步,使用SizedQueue 类能够限制队列的长度。
require 'thread'
queueR = SizedQueue.new(25)
maxSize = queueR.max
puts "队列中的最大的长度是:#{maxSize}"
queueR.max = 78
maxSize = queueR.max
puts "修改后队列中的最大的长度是:#{maxSize}"
执行上述代码,输出结果如下:
队列中的最大的长度是:25
修改后队列中的最大的长度是:78
除上述之外,还可以使用 ConditionVariable 类实现线程同步。