|
76 | 76 | # => ["Jane", "[email protected]", "A", "http://blog.janedoe.com"]
|
77 | 77 | ```
|
78 | 78 |
|
| 79 | +## Thread Safety |
| 80 | + |
| 81 | +When `SQLite3.threadsafe?` returns `true`, then SQLite3 has been compiled to |
| 82 | +support running in a multithreaded environment. However, this doesn't mean |
| 83 | +that all classes in the SQLite3 gem can be considered "thread safe". |
| 84 | + |
| 85 | +When `SQLite3.threadsafe?` returns `true`, it is safe to share only |
| 86 | +`SQLite3::Database` instances among threads without providing your own locking |
| 87 | +mechanism. For example, the following code is fine because only the database |
| 88 | +instance is shared among threads: |
| 89 | + |
| 90 | +```ruby |
| 91 | +require 'sqlite3' |
| 92 | + |
| 93 | +db = SQLite3::Database.new ":memory:" |
| 94 | + |
| 95 | +latch = Queue.new |
| 96 | + |
| 97 | +ts = 10.times.map { |
| 98 | + Thread.new { |
| 99 | + latch.pop |
| 100 | + db.execute "SELECT '#{Thread.current.inspect}'" |
| 101 | + } |
| 102 | +} |
| 103 | +10.times { latch << nil } |
| 104 | + |
| 105 | +p ts.map(&:value) |
| 106 | +``` |
| 107 | + |
| 108 | +Other instances can be shared among threads, but they require that you provide |
| 109 | +your own locking for thread safety. For example, `SQLite3::Statement` objects |
| 110 | +(prepared statements) are mutable, so applications must take care to add |
| 111 | +appropriate locks to avoid data race conditions when sharing these objects |
| 112 | +among threads. |
| 113 | + |
| 114 | +Lets rewrite the above example but use a prepared statement and safely share |
| 115 | +the prepared statement among threads: |
| 116 | + |
| 117 | +```ruby |
| 118 | +db = SQLite3::Database.new ":memory:" |
| 119 | + |
| 120 | +# Prepare a statement |
| 121 | +stmt = db.prepare "SELECT :inspect" |
| 122 | +stmt_lock = Mutex.new |
| 123 | + |
| 124 | +latch = Queue.new |
| 125 | + |
| 126 | +ts = 10.times.map { |
| 127 | + Thread.new { |
| 128 | + latch.pop |
| 129 | + |
| 130 | + # Add a lock when using the prepared statement. |
| 131 | + # Binding values, and walking over results will mutate the statement, so |
| 132 | + # in order to prevent other threads from "seeing" this thread's data, we |
| 133 | + # must lock when using the statement object |
| 134 | + stmt_lock.synchronize do |
| 135 | + stmt.execute(Thread.current.inspect).to_a |
| 136 | + end |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +10.times { latch << nil } |
| 141 | + |
| 142 | +p ts.map(&:value) |
| 143 | + |
| 144 | +stmt.close |
| 145 | +``` |
| 146 | + |
| 147 | +It is generally recommended that if applications want to share a database among |
| 148 | +threads, they _only_ share the database instance object. Other objects are |
| 149 | +fine to share, but may require manual locking for thread safety. |
| 150 | + |
79 | 151 | ## Support
|
80 | 152 |
|
81 | 153 | ### Installation or database extensions
|
|
0 commit comments