Difference between revisions of "Thread-shared Variables"

From AOLserver Wiki
Jump to navigation Jump to search
(fix category entries)
 
(3 intermediate revisions by 3 users not shown)
Line 1: Line 1:
aka Network Shared Variables or NSVs
+
The nsv commands provide a high performance data sharing mechanism. This facility is a more flexible alternative to the obsolete [[ns_share]] command. The model uses an array syntax and includes more features. In addition, lock contention is managed in a much more scalable way--something that is not possible with the obsolete [[ns_share]] facility.
 
 
The nsv commands provide a high performance data sharing mechanism. This facility is much flexible alternative to the obsolete [[ns_share]] command. The model uses an array syntax and includes more features. In addition, lock contention is managed in a much more scalable way--something that is not possible with the obsolete [[ns_share]] facility.
 
 
* [[nsv_get]] - get key value  
 
* [[nsv_get]] - get key value  
 
* [[nsv_exists]] - check key existence  
 
* [[nsv_exists]] - check key existence  
Line 19: Line 17:
 
and to get a value, simply use the nsv_get command:
 
and to get a value, simply use the nsv_get command:
  
     set value [[nsv_get myarray foo]]
+
     set value [nsv_get myarray foo]
  
  
Line 29: Line 27:
  
 
     ns_share ids  
 
     ns_share ids  
     set ids(lock) [[ns_mutex create]]  
+
     set ids(lock) [ns_mutex create]
 
     set ids(next) 0
 
     set ids(next) 0
  
Line 35: Line 33:
 
         ns_share ids  
 
         ns_share ids  
 
         ns_mutex lock $ids(lock)  
 
         ns_mutex lock $ids(lock)  
         set next [[incr ids(next)]]  
+
         set next [incr ids(next)]
 
         ns_mutex unlock $ids(lock)  
 
         ns_mutex unlock $ids(lock)  
 
         return $next  
 
         return $next  
Line 45: Line 43:
  
 
     proc nextid {} {  
 
     proc nextid {} {  
         return [[nsv_incr ids next]]  
+
         return [nsv_incr ids next]
 
     }
 
     }
  
Line 54: Line 52:
 
Another useful feature of nsv is the [[nsv_array]] command which works much like the Tcl array command. This can be used to import and export values from ordinary Tcl arrays. For example, to copy from Tcl use:
 
Another useful feature of nsv is the [[nsv_array]] command which works much like the Tcl array command. This can be used to import and export values from ordinary Tcl arrays. For example, to copy from Tcl use:
  
     nsv_array set meta [[array get tmpmeta]]
+
     nsv_array set meta [array get tmpmeta]
  
 
and to copy to Tcl use:
 
and to copy to Tcl use:
  
     array set metacopy [[nsv_array get meta]]
+
     array set metacopy [nsv_array get meta]
  
 
As with all other nsv command, [[nsv_array]] is atomic and no explicit locking is required. This feature can be used to contruct a new nsv array by first filling up an ordinary temporary Tcl array via some time consuming process and then swapping it into place as above. While the new temporary array is being constructed, other threads can access the old array without delay or inconsistant data. You can even reset a complete nsv array in one step with "reset". For example, instead of:
 
As with all other nsv command, [[nsv_array]] is atomic and no explicit locking is required. This feature can be used to contruct a new nsv array by first filling up an ordinary temporary Tcl array via some time consuming process and then swapping it into place as above. While the new temporary array is being constructed, other threads can access the old array without delay or inconsistant data. You can even reset a complete nsv array in one step with "reset". For example, instead of:
  
 
     ns_share lock meta  
 
     ns_share lock meta  
     set lock [[ns_mutex create]]
+
     set lock [ns_mutex create]
  
 
     ns_mutex lock $lock  
 
     ns_mutex lock $lock  
 
     unset meta  
 
     unset meta  
     array set meta [[array get tmpmeta]]  
+
     array set meta [array get tmpmeta]
 
     ns_mutex unlock $lock
 
     ns_mutex unlock $lock
  
 
you can simply use:
 
you can simply use:
  
     nsv_array reset meta [[array get tmpmeta]]
+
     nsv_array reset meta [array get tmpmeta]
  
 
The reset option will flush and then reset all values atomically, eliminating the need for the explicit lock.
 
The reset option will flush and then reset all values atomically, eliminating the need for the explicit lock.
Line 100: Line 98:
  
 
     ns_share myshare  
 
     ns_share myshare  
     set myshare(lock) [[ns_mutex create]]
+
     set myshare(lock) [ns_mutex create]
  
 
use instead:
 
use instead:
  
     nsv_set myshare lock [[ns_mutex create]]
+
     nsv_set myshare lock [ns_mutex create]
  
 
In your procedures, instead of:
 
In your procedures, instead of:
Line 115: Line 113:
  
 
     proc myproc {} {  
 
     proc myproc {} {  
         ns_mutex lock [[nsv_get myshare lock]]  
+
         ns_mutex lock [nsv_get myshare lock]
 
         ...
 
         ...
  
Line 130: Line 128:
  
 
     <%  
 
     <%  
         ns_puts [[nsv_get myshare key1]]  
+
         ns_puts [nsv_get myshare key1]
 
     %>
 
     %>
  
     <%=[[nsv_get myshare key2]]%>
+
     <%=[nsv_get myshare key2]%>
  
 
Notice that, unlike [[ns_share]], no command is required to define the shared array. The first attempt at setting the variable through any means will automaticaly create the array. Also notice that only arrays are supported. However, to migrate from [[ns_share]] you can simply package up all existing [[ns_share]] scalars into a single array with a short name, perhaps just ".". For example, if you had:
 
Notice that, unlike [[ns_share]], no command is required to define the shared array. The first attempt at setting the variable through any means will automaticaly create the array. Also notice that only arrays are supported. However, to migrate from [[ns_share]] you can simply package up all existing [[ns_share]] scalars into a single array with a short name, perhaps just ".". For example, if you had:
Line 139: Line 137:
 
     ns_share mylock myfile  
 
     ns_share mylock myfile  
 
     set myfile /tmp/some.file  
 
     set myfile /tmp/some.file  
     set mylock [[ns_mutex create]]
+
     set mylock [ns_mutex create]
  
 
you can use:
 
you can use:
  
 
     nsv_set . myfile /tmp/some.file  
 
     nsv_set . myfile /tmp/some.file  
     nsv_set . mylock [[ns_mutex create]]
+
     nsv_set . mylock [ns_mutex create]
  
 
----
 
----
  
[[Category Documentation]] - [[Category Core Tcl API]]
+
[[Category:Documentation]] - [[Category:Core Tcl API]]

Latest revision as of 08:45, 9 February 2009

The nsv commands provide a high performance data sharing mechanism. This facility is a more flexible alternative to the obsolete ns_share command. The model uses an array syntax and includes more features. In addition, lock contention is managed in a much more scalable way--something that is not possible with the obsolete ns_share facility.


Commands for the most part mirror the corresponding Tcl command for ordinary variables. Basically, to set a value, simply use the nsv_set command:

   nsv_set myarray foo $value

and to get a value, simply use the nsv_get command:

   set value [nsv_get myarray foo]


What is the difference between nsv_set and nsv_array set? Is it exactly the same as the difference between set and array set?

Multithreading Features

One advantages of nsv is built in interlocking for thread safety. For example, consider a case of a "increment-by-one" unique id system. Here's the ns_share solution:

   ns_share ids 
   set ids(lock) [ns_mutex create]
   set ids(next) 0
   proc nextid {} { 
       ns_share ids 
       ns_mutex lock $ids(lock) 
       set next [incr ids(next)]
       ns_mutex unlock $ids(lock) 
       return $next 
   }

and here's an nsv solution:

   nsv_set ids next 0
   proc nextid {} { 
       return [nsv_incr ids next]
   }

Note that the nsv solution does not need a mutex as the nsv_incr command is internally interlocked.

Compatibility with Tcl Arrays

Another useful feature of nsv is the nsv_array command which works much like the Tcl array command. This can be used to import and export values from ordinary Tcl arrays. For example, to copy from Tcl use:

   nsv_array set meta [array get tmpmeta]

and to copy to Tcl use:

   array set metacopy [nsv_array get meta]

As with all other nsv command, nsv_array is atomic and no explicit locking is required. This feature can be used to contruct a new nsv array by first filling up an ordinary temporary Tcl array via some time consuming process and then swapping it into place as above. While the new temporary array is being constructed, other threads can access the old array without delay or inconsistant data. You can even reset a complete nsv array in one step with "reset". For example, instead of:

   ns_share lock meta 
   set lock [ns_mutex create]
   ns_mutex lock $lock 
   unset meta 
   array set meta [array get tmpmeta]
   ns_mutex unlock $lock

you can simply use:

   nsv_array reset meta [array get tmpmeta]

The reset option will flush and then reset all values atomically, eliminating the need for the explicit lock.

Other options for the nsv_array command include:

  • nsv_array exists array - test existance of array
  • nsv_array size array - return # of elements in array
  • nsv_array names array ?pattern? - return keys of array

Configuration

The nsv system uses a common multithreading technique to reduce the potential for lock contention which is to split the locks to acheive finer grained locking. This technique groups arrays randomly into buckets and only the arrays within a particular bucket share a lock. The number of buckets to be used can be configured by setting the "nsvbuckets" tcl parameters, e.g.:

   ns/server/server1/tcl 
   nsvbuckets=20

The default is 8 which should be reasonable. Note that you can monitor the lock contention, if any, by enabling mutex metering:

   ns/threads 
   mutexmetering=on

and then viewing the results of "ns_info locks" command after the server has been running for some time. The nsv locks all have names of the form "nsv:##". If you find many lock attempts which did not successed immediately, try increasing nsvbuckets.

Migrating From ns_share

Migrating from ns_share is straightforward. If your init.tcl included commands such as:

   ns_share myshare 
   set myshare(lock) [ns_mutex create]

use instead:

   nsv_set myshare lock [ns_mutex create]

In your procedures, instead of:

   proc myproc {} {
       ns_share myshare
       ns_mutex lock $myshare(lock)  ...

use:

   proc myproc {} { 
       ns_mutex lock [nsv_get myshare lock]
       ...

and within an ADP page, instead of:

   <% 
       ns_share myshare 
       ns_puts $myshare(key1)
   %>
   <%=$myshare(key2)%>

use:

   <% 
       ns_puts [nsv_get myshare key1]
   %>
   <%=[nsv_get myshare key2]%>

Notice that, unlike ns_share, no command is required to define the shared array. The first attempt at setting the variable through any means will automaticaly create the array. Also notice that only arrays are supported. However, to migrate from ns_share you can simply package up all existing ns_share scalars into a single array with a short name, perhaps just ".". For example, if you had:

   ns_share mylock myfile 
   set myfile /tmp/some.file 
   set mylock [ns_mutex create]

you can use:

   nsv_set . myfile /tmp/some.file 
   nsv_set . mylock [ns_mutex create]

-