. .
Functions That Maintain State across Invocations
There are cases where you might wish to use a function to set values in one invocation and use
those same values in another invocation but don't want those values shared by different threads.
When you call strtok(), for example, you first pass it a string to be parsed, and it returns the
pointer to the start of the first token in that string. When you call it a second time (with a NULL
argument), it returns a pointer to the start of the second token, etc. It is highly unlikely that you
would want thread 1 to get the first token in a string and thread 2 to get the second, although this is
exactly what strtok() will do.
There are two possible solutions. One is to write a new function, strtok_r(), which takes an
extra argument that the programmer uses to maintain state explicitly. (This is what POSIX does.)
This is a good technique because the programmer can explicitly choose how to use the arguments
to the best advantage. But at the same time, it puts an additional burden on the programmer, who
must keep track of those arguments, passing them from function to function as required.
The second solution is to use thread-specific data and have strtok() maintain separate state for
each thread (this is what Win32 does). The advantages to this solution are consistency (no code
changes required) and simplicity at the cost of some efficiency.
We'll use rand() again to illustrate these points (Code Example 12-2). Normally, a function like
rand() will be used only occasionally in a program , and there will be very little contention for
its critical section (which is very short anyway). However, should your program happen to call
rand() a great deal, such as in a Monte Carlo simulation, you may experience extensive
contention. By keeping the seed as thread-specific data, this limitation can be avoided.
Example 12-2 Implementing rand() with TSD, Version 2
int rand_2() {
unsigned int *seedp;
int value;
seedp = (int *) pthread_getspecific(rand_key);
value = _rand(seedp);
/* Calculate new value, update seed */
With the rand_2() definition, there is no contention for a critical section (as there is none).
However, even rand_2() is two times slower than rand(). One advantage of rand_1() and
rand_2() is that they don't change the interface of rand(), and existing libraries that use
rand() don't need to be changed.
The semantics of rand_2() are different than those of rand(), inasmuch as pseudo-random
number generators are deterministic, and their results are repeatable when a known seed value is
used. Both rand() and rand_1() would be nondeterministic, as thread scheduling is
nondeterministic. This is unlikely ever to be a problem.
Well, that's interesting, but is it really relevant to Java? Most of the issues above are subsumed by
the use of objects. In Java the object Random contains its own state and is (potentially) just as fast
as rand() in POSIX. So no, these are not terribly relevant to Java, but it's good to know how the
lower-level libraries deal with these issues.
Search WWH :
Custom Search
Previous Page
Multithreaded Programming with JAVA - Topic Index
Next Page
Multithreaded Programming with JAVA - Bookmarks