text:
Multithreaded Python Tutorial with the "Threadworms" Demo
Mon 22 April 2013 Al Sweigart
The code for this tutorial can be downloaded here: threadworms.py or from GitHub. This code works with Python 3 or Python 2, and you need Pygame installed as well in order to run it.
Click the animated gif to view a larger version.
This is a tutorial on threads and multithreaded programs in Python, aimed at beginning programmers. It helps if you know the basics of classes (what they are, how you define methods, and that methods always have self as the first parameter, what subclasses (i.e. child classes) are and how a method can be inherited from a parent class, etc.) Here's a more in-depth classes tutorial.
The example used is a "Nibbles" or "Snake" style clone that has multiple worms running around a grid-like field, with each worm running in a separate thread.
What are threads and why are they useful?
You can skip this section if you already know what threads are and just want to see how to use them in Python.
When you run a normal Python program, the program execution starts at the first line and goes down line by line. Loops and function calls may cause the program execution to jump around, but it is fairly easy to see from the code which line will get executed next at any given point. You can put your finger on the first line of code in the .py file on the screen, and then trace through the next lines of code that are executed. This is single-threaded programming.
However, using multiple threads is like putting a second finger down on your code. Each finger still moves the same way, but now they are executing code simultaneously.
Actually, they aren't executing simultaneously. Your two fingers are taking turns at which one executes code. Computers with multicore CPUs can actually run multiple instructions simultaneously, but there is a feature of Python programs called the GIL (Global Interpreter Lock) that limits a Python program to one core only.
The Python interpreter will run one thread for a while, and then pause it to run another thread for a while. But it does this so fast that it seems like they are running simultaneously.
You can start dozens or hundreds of threads in your program (that's a lot of fingers). This doesn't automatically make your programs dozens or hundreds of times faster though (all the threads are still sharing the same CPU) but it can make your program more efficient.
For example, say you write a function that will download a file full of names, then sorts the names, and then writes these names to a file on your computer. If there are hundreds of files your program needs to process, you would put a call to this function in a loop and it would handle each file serially: download, sort, write, download, sort, write, download, sort, write...
Each of these three steps use different resources on your computer: downloading uses the network connection, sorting uses the CPU, writing the file uses the hard drive. Also, there are tiny pauses within each of these steps. For example, the server you are downloading the file from may be slow and your computer's Internet connection has bandwidth to spare.
It would be better if you could call this function hundreds of times in parallel by using one thread for each file. Not only would this make better use of your bandwidth, but if some files download sooner than others, the CPU can be used to sort them while the network connection continues to work. This makes more efficient use of your computer.
What makes multithreaded programming tricky?
Of course, in the above case, each thread is doing its own separate thing and doesn't need to communicate or synchronize anything with the other threads. You could just write the simple single-threaded version of the download-sort-write program and run the program hundreds of times separately. (Though it might be a pain to type & click each time to run the program each with a different file to download.)
Many multithreaded programs share access to the same variables, but this is where things can get tricky.
Photo from Brad Montgomery)
Here's a common metaphor that is used: Say you have two robot ticket sellers. Their tasks are simple:
- Ask the customer which seat they want.
- Check a list to see if the seat is available.
- Get the ticket for that seat.
- Cross that seat off the list.
A customer asks Robot A for seat 42. Robot A checks that the seat is available from the list and finds that it is, so it grabs the ticket. But before Robot A can cross the seat off the list, Robot B is asked by a different customer for seat 42. Robot B checks the list and sees that the seat is still available, so it tries to grab the ticket for the seat. But Robot B can't find the ticket for seat 42. THIS DOES NOT COMPUTE, and Robot B's electronic brain explodes. Robot A then crosses seat 42 off of the list.
The above problem happens because although the two robots (or rather, two threads) are executing independently, they are both reading and modifying a shared list (or rather, a variable). Your programs can get very hard-to-fix bugs which are also difficult to even reproduce, since Python's thread execution switching is nondeterministic, that is, done differently each time the program is run. We aren't used to having the data in variables "magically" change from one line to the next just because a different thread was executed in between them.
When the execution switches from one thread to another, this is known as a context switch.
There is also the problem of deadlocks, which is commonly explained using the metaphor of the Dining Philosophers. Five philosophers are sitting around a circular table eating spaghetti but require two forks to do so. There is one fork between each philosopher (for a total of five forks). The method the philosophers use to eat is this:
- Philosophize for a while.
- Pick up the fork on your left.
- Wait until the fork on your right is available.
- Pick up the fork on your right.
- Eat.
- Put the forks down.
- Go back to step 1.
Aside from the fact that they'll be sharing forks with their neighbors (eww), it seems like this method will work. But sooner or later everyone at the table will end up with the fork on their left in their hand and waiting for the fork on their right. But because everyone is holding on to the fork their neighbor is waiting for and won't put it down until they've eaten, the philosophers are in a deadlock state. They will be holding forks in their left hand but never getting a fork in their right hand, so they never eat and never put down the fork in their left hand. The philosophers all starve to death (except for Voltaire who is actually a robot. Without spaghetti, his electronic brain explodes.)
There is also a similar situation called a livelock. This is when no work gets done because the threads are too generous at making a resource available. The best metaphor of this is when two people are walking towards each other down a hall. They step to the side to let the other person walk past, but end up blocking each other. So they both step back to the other side, but end up blocking each other again. They continue doing this until they starve/electronic-brain-explode.
There are a few other problems that can come up with multithreaded programming such as starvation (no seriously, that's what it is called) and generally fall under the label of "Concurrency" in computer science. But we will only treat a simplified case.
One way to prevent bugs with multithreaded programming is by using locks. Before a thread reads or modifies a shared variable, it attempts to "acquire" a lock. If it can acquire the lock, the thread goes on to read or modify the variable. If the thread cannot acquire the lock, it waits until the lock becomes available.
When the thread is done with the shared variable, it will "release" the lock so that some other thread waiting for the lock can acquire it.
Going back to our robot ticket seller metaphor, this is like having a robot pick up the list (the list is a "lock"), and then reading it the ticket is available, grabbing the ticket, and then crossing out the seat on the list. When the robot puts the list back down, it is "releasing the lock". If another robot needs to pick up the list but it is not there, it will wait until the list is available.
You can cause bugs by writing code that forgets to release a lock. This will cause a deadlock situation since the other threads will hang and do nothing while waiting for a lock to bereleased.
Threads in Python
Okay, let's write a Python program that demonstrates how to use threads and locks. This program is based off of my "Snake" clone in Chapter 6 of my Making Games with Python & Pygame book. Except instead of a worm running around eating apples, we'll just have the worm running around the screen. And instead of just one worm, we will have multiple worms. Each worm will be controlled by a separate thread. The shared variable will have the data structure that represents which places on the screen (called "cells" in this program) are occupied by a worm. A worm cannot move forward to occupy a cell if another worm is already there. We will use locks to ensure that the worms don't occupy the same cell as another worm.
The code for this tutorial can be downloaded here: threadworms.py or from GitHub. This code works with Python 3 or Python 2, and you need Pygame installed as well in order to run it.
Here's a summary of the thread-related code in our threadworms.py program:
import threading
Python's thread library is in a module named
threading, so first import this module.
GRID_LOCK = threading.Lock()
The class Lock in the threading module has
acquire() and
release() methods. We will create a new
Lock object and store it in a global variable named
GRID_LOCK. (Since the state of the grid-like screen and which cells are occupied is stored in a global variable named
GRID. The pun was unintended.)
# A global variable that the Worm threads check to see if they should exit. WORMS_RUNNING = True
WORMS_RUNNING global variable is regularly checked by the worm threads to see if they should quit. Calling
sys.exit() will not stop the program because it only quits the thread that made the call. As long as there are other threads still running the program will continue. The main thread in our program (which handles the Pygame drawing and event handling) will set
WORMS_RUNNING to
False before it calls
pygame.quit() and
sys.exit(). The next time a thread checks
WORMS_RUNNING, it will quit, until eventually the last thread quits and then the program terminates.
class Worm(threading.Thread): def __ init__(self, name='Worm', maxsize=None, color=None, speed=None): threading.Thread.__init__(self) self.name = name
The thread's code must start from a class that is a child of the
Thread class (which is in the
threading module). Our
Thread subclass will be named
Worm since it controls You don't need an
__init__() function, but since our Worm classes uses one we need to call the
threading.Thread class's
__init__() method first. Also optional is to override the name member. Our
__init__() function uses the string
'Worm' by default, but we can supply each thread with a unique name. Python will display the thread's name in the error message if it crashes.
GRID_LOCK.acquire() # ...some code that reads or modifies GRID... GRID_LOCK.release()
Before we read or modify the value in the
GRID variable, the thread's code should attempt to acquire the lock. If the lock isn't available, the method call to
acquire() will not return and instead "block" until the lock becomes available. The thread is paused while this happens. This way, we know that the code after the
acquire() call will only happen if the thread has acquired the lock.
Acquiring and releasing a lock around a bit of code ensures that another thread does not execute this code while the current thread is. This makes the code atomic because the code is always executed as a single unit.
After the thread's code is done with the
GRID variable, the lock can be released by calling the
release() method.
def run(self): # thread code goes here.
A thread starts when the
Worm class (which is a subclass of
threading.Thread) has its
start() method called. We don't have to implement
start() in the
Worm class because it is inherited from the
threading.Thread class. When the
start() method is called, a new thread is created and the code inside the
run() method is executed in this new thread. Do not call the
run() method directly, as this won't create the new thread.
This is important to know: to start the thread call the
start() method, but the code that gets run in the new thread is in
run(). We don't have to define
start() because it is inherited from
threading.Thread. We do need to define
run() since that is where our thread's code will go.
When the
run() method returns (or
sys.exit() is called in the thread), the thread will be destroyed. All threads in a program must be destroyed before the program terminates. The program will still be running as long as there is one running thread.
So when
start() is called, this is when you would place your second finger on the source code in
run() to start tracing the code. Your first finger will continue tracing the code after the line that has the
start() call.
A Simple Multithreaded Example
Before we go into the Threadworm code, let's just look at a dead simple multithreaded program:
import threading TOTAL = 0 class CountThread(threading.Thread): def run(self): global TOTAL for i in range(100): TOTAL = TOTAL + 1 print('%s\n' % (TOTAL)) a = CountThread() b = CountThread() a.start() b.start()
This program defines a new class called
CountThread. When a
CountThread object's
start() method is called, a new thread is created which will loop 100 times and increment the
TOTAL global variable (which is shared between the variables) by
1 on each iteration of the loop.
Since we are creating two
CountThread objects, whichever one finishes last should display
200. Each thread increases
TOTAL by
100 and there are two threads. When we run this program, that's what we see:
100 200
Because the first number is
100, we can tell that probably what happened is that one thread ran through the entire loop before a context switch happened.
However, if we change
range(100) to
range(100000), we would expect the second number to be
200000, since each thread increases
TOTAL by
100000 and there are two threads. But when we run the program, something like this appears (your numbers may be slightly different):
143294 149129
That second number is not
200000! It's quite less than that actually. The reason this happened is because we did not use locks around the code the reads and modifies the
TOTAL variable, which is shared among multiple threads.
Look at this line:
TOTAL was set to
99, then you would expect
TOTAL + 1 to evaluate to
99 + 1 and then to
100, and then
100 is stored as the new value in
TOTAL. Then on the next iteration,
TOTAL + 1 would be
100 + 1 or
101, which is stored as the new value in
But say when
TOTAL + 1 gets evaluated as
99 + 1, the execution switches to the other thread, which is also about to execute the
TOTAL = TOTAL + 1 line. The value in
TOTAL is still
99, so
TOTAL + 1 in this second thread gets evaluated to
99 + 1.
Then, another context switch happens back to the first thread where TO
TAL = 99 + 1 is in the middle of being executed. The integer
100 is assigned to
TOTAL. Now execution switches back to the second thread again.
In this second thread,
TOTAL = 99 + 1 is about to be executed. Even though
TOTAL is now
100, the
TOTAL + 1 in this second thread has already been evaluated as
99 + 1. So the second thread also ends up assigning the integer
100 to
TOTAL. Even though this
TOTAL = TOTAL + 1 has been executed twice (once by each thread), the value in
TOTAL has really only been incremented by
The problem is, the line of code
TOTAL = TOTAL + 1 is not atomic. The context switch can happen right in the middle of the line being executed. We need to use locks around this code to make this an atomic operation.
This new code fixes this problem:
import threading TOTAL = 0 MY_LOCK = threading.Lock() class CountThread(threading.Thread): def run(self): global TOTAL for i in range(100000): MY_LOCK.acquire() TOTAL = TOTAL + 1 MY_LOCK.release() print('%s\n' % (TOTAL)) a = CountThread() b = CountThread() a.start() b.start()
When we run this code, this is what is outputted (your first number might be a little different):
199083 200000
That the second number is
200000 tells us that the
TOTAL = TOTAL + 1 line was correctly executed each of the 200,000 times it was run.
Explaining the Threadworms Program
I'm going to use the threadworms_nocomments.py version of the program since it doesn't have the very verbose comments in it. The line numbers have been included at the front of each line (they are not a part of the actual Python source code). I skip a lot of the commented sections because they are self-explanatory. You don't really need to know Pygame to follow this code. Pygame is only responsible for creating the window and drawing the lines and rectangles on it.
One thing to know is that Pygame uses a tuple of three integers to represent colors. The integers each span from
0 to
255 and represent the RGB (Red-Green-Blue) value of the color. So
(0, 0, 0) is black and
(255, 255, 255) is white and
(255, 0, 0) is red and
(255, 0, 255) is purple, etc.
9. import random, pygame, sys, threading 10. from pygame.locals import * 11. 12. # Setting up constants 13. NUM_WORMS = 24 # the number of worms in the grid 14. FPS = 30 # frames per second that the program runs 15. CELL_SIZE = 20 # how many pixels wide and high each "cell" in the grid is 16. CELLS_WIDE = 32 # how many cells wide the grid is 17. CELLS_HIGH = 24 # how many cells high the grid is
The top part of the code imports some modules our program needs and defines some constant values. Feel free to edit these constant values. Increasing or decreasing the
FPS value doesn't change how fast the worms run around, it just changes how often the screen updates. If you set this value very low, it looks like the worms are teleporting since they move multiple spaces in between screen updates.
CELL_SIZE is how big each square on the screen's grid is (in pixels). If you want to change the number of cells, modify the
CELLS_WIDE and
CELLS_HIGH constants.
20. GRID = [] 21. for x in range(CELLS_WIDE): 22. GRID.append([None] * CELLS_HIGH)
GRID global variable will contain data that tracks the state of the grid. It is a simple list of lists so that
GRID[x][y] will refer to the cell at the X and Y coordinate. (In programming, the (0, 0) origin is at the top-left corner of the screen. X increases going to the right (just like in mathematics classes) but Y increases going down.)
GRID[x][y] is set to None, then that cell is unoccupied. Otherwise,
GRID[x][y] will be set to an RGB triplet. (This information is used when drawing the grid to the screen.)
24. GRID_LOCK = threading.Lock() # pun was not intended
Line 24 creates a Lock object which our threads' code will acquire and release before reading or modifying
26. # Constants for some colors. 27. # R G B 28. WHITE = (255, 255, 255) 29. BLACK = ( 0, 0, 0) 30. DARKGRAY = ( 40, 40, 40) 31. BGCOLOR = BLACK # color to use for the background of the grid 32. GRID_LINES_COLOR = DARKGRAY # color to use for the lines of the grid RGB tuples are kind of hard to read, so I usually set up some constants for them. 33. 34. # Calculate total pixels wide and high that the full window is 35. WINDOWWIDTH = CELL_SIZE * CELLS_WIDE 36. WINDOWHEIGHT = CELL_SIZE * CELLS_HIGH 37. 38. UP = 'up' 39. DOWN = 'down' 40. LEFT = 'left' 41. RIGHT = 'right'
Some more simple constants. I use constants like
DOWN and
RIGHT instead of strings like
'down' and
'right' because if I make a typo using constants (i.e.
DWON) then Python will immediately crash with a
NameError exception. This is much better than if I make a typo like
'dwon' which won't immediately crash the program will cause bugs later on, making it more difficult to track down.
43. HEAD = 0 44. BUTT = -1 # negative indexes count from the end, so -1 will always be the last index
Each worm will be represent by a list of dictionaries like
{'x': 42, 'y': 7}. Each of these dictionaries represents a single body segment of the worm. The dictionary at the front of the list (at index
0) is the head and the dictionary at the end of the list (at index
-1, using Python's nice negative indexing which begins counting from the end) is the butt of the worm.
(In computer science, "head" often refers to the first item in a queue or list, and "tail" refers to every item after the head. So I use "butt" to refer to just the last item. Also, I am silly.)
The above worm would be represented with a list that looks like this:
[{'x': 7, 'y': 2}, {'x': 7, 'y': 3}, {'x': 7, 'y': 4}, {'x': 8, 'y': 4}, {'x': 9, 'y': 4}, {'x': 10, 'y': 4}, {'x': 11, 'y': 4}, {'x': 11, 'y': 3}, {'x': 11, 'y': 2}]
46. # A global variable that the Worm threads check to see if they should exit. 47. WORMS_RUNNING = True
As long as one thread is running, the program will continue to execute. The main thread that does the screen drawing will also detect when the user has clicked the close button on the window or pressed the Esc key, so it needs a way to tell the worm threads to quit. We will code the worm threads to constantly check
WORMS_RUNNING. If
WORMS_RUNNING is set to
False, then the thread will terminate itself.
49. class Worm(threading.Thread): # "Thread" is a class in the "threading" module. 50. def __init__(self, name='Worm', maxsize=None, color=None, speed=None):
Here's our
Worm class. It is a child class of the
threading.Thread class. Each worm can have a name (which appears if the thread crashes, helping us identify which thread crashed), and a size, color, and speed. Default values are provided, but we can specify these ourselves if we want.
56. threading.Thread.__init__(self) # since we are overriding the Thread class, we need to first call its __init__() method.
Since we are overriding the
__init__() method, we need to call the parent classes
__init__() method so that it can initialize all the thread stuff. (We don't need to know how it works, just remember to call it.)
57. 58. self.name = name 59. 60. # Set the maxsize to the parameter, or to a random maxsize. 61. if maxsize is None: 62. self.maxsize = random.randint(4, 10) 63. 64. # Have a small chance of a super long worm. 65. if random.randint(0,4) == 0: 66. self.maxsize += random.randint(10, 20) 67. else: 68. self.maxsize = maxsize 69. 70. # Set the color to the parameter, or to a random color. 71. if color is None: 72. self.color = (random.randint(60, 255), random.randint(60, 255), random.randint(60, 255)) 73. else: 74. self.color = color 75. 76. # Set the speed to the parameter, or to a random number. 77. if speed is None: 78. self.speed = random.randint(20, 500) # wait time before movements will be between 0.02 and 0.5 seconds 79. else: 80. self.speed = speed
The above code sets up a worm with random values for the size, color, and speed unless specific values were specified for the parameters.
82. GRID_LOCK.acquire() # block until this thread can acquire the lock 83. 84. while True: 85. startx = random.randint(0, CELLS_WIDE - 1) 86. starty = random.randint(0, CELLS_HIGH - 1) 87. if GRID[startx][starty] is None: 88. break # we've found an unoccupied cell in the grid 89. 90. GRID[startx][starty] = self.color # modify the shared data structure 91. 92. GRID_LOCK.release()
We need to determine a random starting location for the worm. To make this easier, all worms begin with a length of one body segment and grow until they reach their full maximum size. But we need to make sure that the random location on the grid we come up with isn't already occupied. This involves reading and modifying the
GRID global variable, so we need to acquire and release the
GRID_LOCK lock before doing this.
(As a side note, you might be wondering why we don't have a "global GRID" line at the beginning of this method.
GRID is a global variable and we are modifying it in this method, and without a
global statement Python should consider this a local variable that just happens to have the same name as the
GRID global variable. But if you look closer, we only change values inside the
GRID list of lists, but never the value in
GRID itself. That is, we have code that looks like "
GRID[startx][starty] = self.color" but never "
GRID = someValue". Because we don't actually modify
GRID itself, Python considers the use of the variable name
GRID in this method to refer to the global variable
We keep looping until we've found an unoccupied cell, and then mark that cell as now occupied. After this, we are done reading and modifying
GRID so we release the
GRID_LOCK lock.
(Another side note, if there are no free cells on the grid, this loop will continue to loop forever and the thread will "hang". Since the other threads will continue to run, you might not notice this problem. The new worm will not be created but the rest of the program continues to run normally. However, when you try to quit, since the hanging thread never gets to check
WORMS_RUNNING to know it should quit and the program will refuse to terminate. You will have to force the program to shut down through your operating system. Just be sure not to add more worms than you have space for.)
96. self.body = [{'x': startx, 'y': starty}] 97. self.direction = random.choice((UP, DOWN, LEFT, RIGHT))
The starting body segment is added to the
body member variable. The
body member variable will be a list of all the locations of segments of the body. The direction that the worm is heading in is stored in the
direction member variable.
Technically, since this worm right now only has one body segment that is both the first and last item in the list, the worm's head is the same as its butt.
100. def run(self): 101. while True: 102. if not WORMS_RUNNING: 103. return # A thread terminates when run() returns.
run() method is the method that is called when the worm's
start() method is called. The code in
run() is executed in a brand new thread. We will have an infinite loop that causes the worm to continuously move around the grid. The first thing we do on each iteration of the loop is check if
WORMS_RUNNING is set to
False, and if so, we should return from this method.
The thread will terminate itself if we either call
sys.exit() from the thread or when the
run() method returns.
105. # Randomly decide to change direction 106. if random.randint(0, 100) < 20: # 20% to change direction 107. self.direction = random.choice((UP, DOWN, LEFT, RIGHT))
On each move, there's a 20% chance that the worm randomly changes direction. (Although the new direction could be the same as the current direction. But I wanted to write this code out quickly.)
109. GRID_LOCK.acquire() # don't return (that is, block) until this thread can acquire the lock 110. 111. nextx, nexty = self.getNextPosition() 112. if nextx in (-1, CELLS_WIDE) or nexty in (-1, CELLS_HIGH) or GRID[nextx][nexty] is not None: 113. # The space the worm is heading towards is taken, so find a new direction. 114. self.direction = self.getNewDirection() 115. 116. if self.direction is None: 117. # No places to move, so try reversing our worm. 118. self.body.reverse() # Now the head is the butt and the butt is the head. Magic! 119. self.direction = self.getNewDirection() 120. 121. if self.direction is not None: 122. # It is possible to move in some direction, so reask for the next postion. 123. nextx, nexty = self.getNextPosition() 124. 125. if self.direction is not None: 126. # Space on the grid is free, so move there. 127. GRID[nextx][nexty] = self.color # update the GRID state 128. self.body.insert(0, {'x': nextx, 'y': nexty}) # update this worm's own state 129. 130. # Check if we've grown too long, and cut off tail if we have. 131. # This gives the illusion of the worm moving. 132. if len(self.body) > self.maxsize: 133. GRID[self.body[BUTT]['x']][self.body[BUTT]['y']] = None # update the GRID state 134. del self.body[BUTT] # update this worm's own state (heh heh, worm butt) 135. else: 136. self.direction = random.choice((UP, DOWN, LEFT, RIGHT)) # can't move, so just do nothing for now but set a new random direction 137. 138. GRID_LOCK.release()
The above code handles moving the worm one space. Since this involves reading and modifying
GRID, we need to acquire the
GRID_LOCK lock first. Essentially, the worm will try to move one space in the direction that it's direction member variable says. If this cell is beyond the border of the grid or is already occupied, then the worm will change its direction. If the worm is blocked on all sides, then the worm reverses itself so that the butt becomes the head and the head becomes the butt. If the worm still can't move in any direction, then it will just stay put for now.
140. pygame.time.wait(self.speed)
After the worm has moved one space (or at least tried to), we will put the thread to sleep. Pygame has a function called
wait() that does the same thing as
time.sleep(), except that the argument to
wait() is in integer of milliseconds instead of seconds.
pygame.time.wait() and the Python Standard Library's
time.time() functions (and Pygame's
tick() method) are smart enough to tell the operating system to put the thread to sleep for a while and just run other threads instead. Of course, while the OS could interrupt our thread at any time to hand execution off to a different thread, calling
wait() or
sleep() is a way we can explicitly say, "Go ahead and don't run this thread for X milliseconds."
This wouldn't happen if we have "wait" code like this:
startOfWait = time.time() while time.time() - 5 > startOfWait: pass # do nothing for 5 seconds
The above code also implements "waiting", but to the OS it looks like your thread is still executing code (even though this code is doing nothing but looping until 5 seconds has passed). This is inefficient, because time spent executing the above pointless loop is time that could have been spent executing other thread's code.
Of course, if ALL worms' threads are sleeping, then the computer can know it can use the CPU to run other programs besides our Python Threadworms script.
143. def getNextPosition(self): 144. # Figure out the x and y of where the worm's head would be next, based 145. # on the current position of its "head" and direction member. 146. 147. if self.direction == UP: 148. nextx = self.body[HEAD]['x'] 149. nexty = self.body[HEAD]['y'] - 1 150. elif self.direction == DOWN: 151. nextx = self.body[HEAD]['x'] 152. nexty = self.body[HEAD]['y'] + 1 153. elif self.direction == LEFT: 154. nextx = self.body[HEAD]['x'] - 1 155. nexty = self.body[HEAD]['y'] 156. elif self.direction == RIGHT: 157. nextx = self.body[HEAD]['x'] + 1 158. nexty = self.body[HEAD]['y'] 159. else: 160. assert False, 'Bad value for self.direction: %s' % self.direction 161. 162. return nextx, nexty
getNextPosition() figures out where the worm will go next given the position of its head and the direction it is going.
165. def getNewDirection(self): 166. x = self.body[HEAD]['x'] # syntactic sugar, makes the code below more readable 167. y = self.body[HEAD]['y'] 168. 169. # Compile a list of possible directions the worm can move. 170. newDirection = [] 171. if y - 1 not in (-1, CELLS_HIGH) and GRID[x][y - 1] is None: 172. newDirection.append(UP) 173. if y + 1 not in (-1, CELLS_HIGH) and GRID[x][y + 1] is None: 174. newDirection.append(DOWN) 175. if x - 1 not in (-1, CELLS_WIDE) and GRID[x - 1][y] is None: 176. newDirection.append(LEFT) 177. if x + 1 not in (-1, CELLS_WIDE) and GRID[x + 1][y] is None: 178. newDirection.append(RIGHT) 179. 180. if newDirection == []: 181. return None # None is returned when there are no possible ways for the worm to move. 182. 183. return random.choice(newDirection)
getNewDirection() method returns a direction (one of the
LEFT, or
RIGHT strings) that is for an unoccupied cell within the grid. If there are no available cells the head could move towards, the method returns None.
185. def main(): 186. global FPSCLOCK, DISPLAYSURF 187. 188. # Draw some walls on the grid 189. squares = """ 190. ........................... 191. ........................... 192. ........................... 193. .H..H..EEE..L....L.....OO.. 194. .H..H..E....L....L....O..O. 195. .HHHH..EE...L....L....O..O. 196. .H..H..E....L....L....O..O. 197. .H..H..EEE..LLL..LLL...OO.. 198. ........................... 199. .W.....W...OO...RRR..MM.MM. 200. .W.....W..O..O..R.R..M.M.M. 201. .W..W..W..O..O..RR...M.M.M. 202. .W..W..W..O..O..R.R..M...M. 203. ..WW.WW....OO...R.R..M...M. 204. ........................... 205. ........................... 206. """ 207. #setGridSquares(squares)
setGridSquares() function can be used to draw static blocks on the grid by passing a multiline string. The period characters represent no change, a space character means "set this to be unoccupied" and any other character will represent a static block to place on the grid. You can uncomment line 207 if you want to see the "Hello worm" text written out in blocks.
209. # Pygame window set up. 210. pygame.init() 211. FPSCLOCK = pygame.time.Clock() 212. DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) 213. pygame.display.set_caption('Threadworms')
This is standard Pygame setup code to create a window for our program.
215. # Create the worm objects. 216. worms = [] # a list that contains all the worm objects 217. for i in range(NUM_WORMS): 218. worms.append(Worm()) 219. worms[-1].start() # Start the worm code in its own thread.
This code creates the
Worm objects and then creates their threads by calling the
start() method. The code in each worm's
run() method will begin executing in a separate thread at this point.
221. while True: # main game loop 222. handleEvents() 223. drawGrid() 224. 225. pygame.display.update() 226. FPSCLOCK.tick(FPS)
The main game loop is pretty simple. The
handleEvents() function will be checking if the user is terminating the program and the
drawGrid() function will draw the grid lines and cells to the screen. The
pygame.display.update() function tells the window to update the screen, after which the
tick() method will pause for however long is needed to achieve the framerate specified in
229. def handleEvents(): 230. # The only event we need to handle in this program is when it terminates. 231. global WORMS_RUNNING 232. 233. for event in pygame.event.get(): # event handling loop 234. if (event.type == QUIT) or (event.type == KEYDOWN and event.key == K_ESCAPE): 235. WORMS_RUNNING = False # Setting this to False tells the Worm threads to exit. 236. pygame.quit() 237. sys.exit()
The Pygame events can tell us when the user has pressed the Esc key or clicked on the close button for the window. In this case we want to set
WORMS_RUNNING to
False so that the threads will terminate themselves and then the main thread shuts down Pygame and exits.
240. def drawGrid(): 241. # Draw the grid lines. 242. DISPLAYSURF.fill(BGCOLOR) 243. for x in range(0, WINDOWWIDTH, CELL_SIZE): # draw vertical lines 244. pygame.draw.line(DISPLAYSURF, GRID_LINES_COLOR, (x, 0), (x, WINDOWHEIGHT)) 245. for y in range(0, WINDOWHEIGHT, CELL_SIZE): # draw horizontal lines 246. pygame.draw.line(DISPLAYSURF, GRID_LINES_COLOR, (0, y), (WINDOWWIDTH, y))
This code draws the screen based on the values in
GRID. But first it draws the grid lines.
248. # The main thread that stays in the main loop (which calls drawGrid) also 249. # needs to acquire the GRID_LOCK lock before modifying the GRID variable. 250. GRID_LOCK.acquire() 251. 252. for x in range(0, CELLS_WIDE): 253. for y in range(0, CELLS_HIGH): 254. if GRID[x][y] is None: 255. continue # No body segment at this cell to draw, so skip it 256. 257. color = GRID[x][y] # modify the GRID data structure 258. 259. # Draw the body segment on the screen 260. darkerColor = (max(color[0] - 50, 0), max(color[1] - 50, 0), max(color[2] - 50, 0)) 261. pygame.draw.rect(DISPLAYSURF, darkerColor, (x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE )) 262. pygame.draw.rect(DISPLAYSURF, color, (x * CELL_SIZE + 4, y * CELL_SIZE + 4, CELL_SIZE - 8, CELL_SIZE - 8)) 263. 264. GRID_LOCK.release() # We're done messing with GRID, so release the lock.
Because this code reads the
GRID variable, we will first acquire the
GRID_LOCK lock. If a cell is occupied (that is, it is set to an RGB tuple value inside the
GRID variable) the code draws in the cell.
267. def setGridSquares(squares, color=(192, 192, 192)): 268. # squares is set to a value like: 269. # """ 270. # ...... 271. # ...XX. 272. # ...XX. 273. # ...... 274. # """ 275. 276. squares = squares.split('\n') 277. if squares[0] == '': 278. del squares[0] 279. if squares[-1] == '': 280. del squares[-1] 281. 282. GRID_LOCK.acquire() 283. for y in range(min(len(squares), CELLS_HIGH)): 284. for x in range(min(len(squares[y]), CELLS_WIDE)): 285. if squares[y][x] == ' ': 286. GRID[x][y] = None 287. elif squares[y][x] == '.': 288. pass 289. else: 290. GRID[x][y] = color 291. GRID_LOCK.release()
The setGridSquares() can write static blocks to the grid and was explained previously.
294. if __name__ == '__main__': 295. main()
The above is a Python trick. Instead of putting the main code in the global scope, we put it into a function named
main() which is called from the bottom. This guarantees that all the functions have been defined before the code in
main() runs. The
__name__ variable is only set to the string
'__main__' if this program was run itself, as opposed to imported as a module by another program.
That's it! Multithreaded programming is fairly simple to explain, but it can be tricky to understand how to get your own multithreaded programs to work correctly. The best way to learn is to practice by writing your own programs.
Actually, the way we have our code set up, even if we got rid of the locks it would still run almost perfectly. Nothing would crash, although there would sometimes be the case where two worms are approaching the same cell and end up both occupying it. They would then seemingly move through each other. Using locks ensures that only one worm can occupy a cell at any time.
Good luck!


text:
Lemon Ice Feminized
Lemon Ice Feminized Cannabis Seeds
Lemon Ice feminized a top shelf Sativa hybrid. Known for her light sweet, lemony taste and speedy uplifting high.
Lemon Ice Feminized Cannabis Seeds
Lemon Ice feminized is not a simple strain by any means. Get the complex indica/sativa hybrid cerebral high without the anxiety of been really high, when you're, well… really, really high. She loves the sunshine, so don't keep her in the shade when growing her outdoors. She'll make you the sweetest compact nuggets. All of these God-Like Nugs are top shelf quality bud, and with decent yields.
There are no reviews yet.

subset: commoncrawl


text:
1. Re: Passing data to a work item handlerswiderski.maciej Apr 23, 2012 3:32 AM (in response to out_sider)
As you already found out yourself, Work Item definition is only a structure of the work item rather than the instance of it. Why not let users to fill in those details on process in web designer if they are constant strings and will apply to all instances of that process or on process instance start if these values are instance specific?
2. Re: Passing data to a work item handlerout_sider Apr 23, 2012 8:49 AM (in response to swiderski.maciej)
Thanks for the reply and suggestion.
The reason for wanting to define the work item data outside the web designer is due to the complexity of the data.
A rather complex form is necessary to define the atributes of the Service Task (represented by the Work Item Definition) and doing that assignment using the Drools Web Designer built in form isn't very intuitive.
Although custom forms seem to be planned for future release, for now I would still need to do some sorte of javascript hack to catch click events on the designer and launch my form (at least that's what I undestood).
What I'm thinking of doing is considering that each filled form will be a Service Task I can fetch my data structure using the workItem.getName();...I'm hopping it will work .
3. Re: Passing data to a work item handleratran Apr 18, 2013 4:25 AM (in response to out_sider)
I am also interested in this feature. Indeed, I think it is a good idea to pre-fill an attribute with a default value so that users don't have to fill everything and change only what they want. Mr. Maciej, do you think it will be easy to do that? Thank you.
EDIT: I found out that a plugin implementation could do this.


text:
Tsunami Warning Lifted Following Strong Japan Earthquake [VIDEO]
Japan's Meteorological Agency has lifted a tsunami warning for the country's northeastern coast.
The warning was issued after a magnitude 7.3 quake struck offshore, swaying buildings across much of Japan. There were no immediate reports of serious damage but several people were reportedly hurt.
One coastal city reported that a tsunami of three feet hit about 45 minutes after the quake.
Shortly before the earthquake struck, NHK television broke off regular programming to warn that a strong quake was due to hit. The country's Meteorological Agency has an early warning system using data from seismographs scattered across Japan. Afterward, the announcer repeatedly urged all near the coast to flee to higher ground.
Tokyo Electric Power says there have been no problems at any of its nuclear plants.
(Copyright 2012 by The Associated Press. All Rights Reserved.)


text:
by David William Pear
If you want to know the untold history of the U.S.A., then a good place to start is with the history of US imperialism in Asia from the mid-19th century until today. Not only will that reveal the history of the criminality of US foreign policy, but it will also reveal the true nature of U.S. capitalism, imperialism, fascism and U.S. wars of aggression: past, present and future.
For centuries the U.S. has preached that it believes in democracy, freedom and self-determination, but its actions towards other countries speak louder than words. Internationally the U.S. is a predator and a bully. It subjugates small countries, corrupts them by backing right-wing dictators, and enables death squads to commit mass murder of all suspected dissidents. During the First Cold War leftists, anti-colonialists, nationalists and intellectuals were called "communists" and imprisoned, tortured and executed. Now they are called "terrorists".
The foreign policy interests of the U.S. are to promote the neocolonial interests of U.S. corporations, and to project the financial and military power of the U.S. internationally. If the U.S. cannot bully a head of state into collaborating then it backs a military coup d'etat, stirs up internal violence with divide and conquer strategies, and covertly uses mercenaries to start civil wars. If all else fails it will find a pretext or a false flag to invade and overthrow an unfriendly government.
Because of past disasters in Korea, Vietnam, and Iraq the U.S. is hesitant to commit large numbers of U.S. boots on the ground. Instead it prefers the safety of "boots in the sky" with its domination of air power to bomb helpless countries into submission. If it cannot get a country to submit then it destroys it mercilessly as an example of the price that other countries will pay if they do not go along to get along with the U.S.A. The U.S. never gives up on its quest for control of worldwide capitalism. Any doubters of U.S. persistence need only to look to North Korea, Cuba and Iran.
U.S. foreign policy is the domain of the elites. The public busies itself with domestic issues, just trying to make a living and raise a family. Sometimes the elites of foreign policy let their masks slip to reveal their true nature. When Zbigniew Brzezinski said in 2009 that "today it is infinitely easier to kill a million people than to control a million people" he was not just intellectualizing, as if teaching a Harvard course on political science. Brzezinski was admitting to his personal responsibility for policies that have killed millions of people.
It was Brzezinski that advised President Jimmy Carter to destabilize Afghanistan in the 1980's. At the time Afghanistan's communist government was modernizing the country, developing its economy, educating its people, and improving the standard of living for millions of Afghans. It was also advancing the rights and opportunities for women, which the U.S. is constantly touting as one of its cherished human rights concerns. It was Brzezinski's destabilization project that set women's rights back hundreds of years in Afghanistan. When human rights get in the way of U.S. foreign policy objectives, then human rights lose.
In 1979 Brzezinski advised Carter to secretly authorize the CIA to give financial and military aid to further inflame Islamic fanatic mujahideen that were violently opposed to modernization. When the Soviet Union intervened militarily in support of the threatened Afghan government it was not an invasion. It was the legal response of Russia to a neighboring country that was asking for military aid against foreign backed insurgents. Today we see a similar Russian military assistance program in Syria for similar reasons against very similar villains.
Carter feigned indignation and outrage towards Russia and he put the freeze back into the Cold War. He decried the "invasion as a deliberate effort by a powerful atheistic government to subjugate an independent Islamic people", and Carter claimed it was a Russian plot to control Afghanistan's oil.
Carter declared a U.S. boycott of the 1980 Moscow-hosted Olympics to punish Russia, and by doing so he dashed the dreams of U.S. athletes that had been training for 4 years in preparation for the Olympic Games. A good time was had by all in Moscow without the U.S. participation. U.S. athletes were sacrificed as pawns in Brzezinski's game of the Grand Chessboard. It is a blood soaked chessboard where the masters see flesh and blood people as objects to be toyed with.
What Brzezinski and Carter did was to set a trap that the Soviets fell into when they sent their military into Afghanistan. The trap had been laid before the Russian "invasion", and not afterwards as Brzezinski would brag years later in a 1998 interview with Le Nouvel Observateur:
Yes. According to the official version of history, CIA aid to the Mujahiddin began during 1980, that is to say, after the Soviet army invaded Afghanistan on December 24, 1979. But the reality, closely guarded until now, is completely otherwise: Indeed, it was July 3, 1979 that President Carter signed the first directive for secret aid to the opponents of the pro-Soviet regime in Kabul. And that very day, I wrote a note to the president in which I explained to him that in my opinion this aid was going to induce a Soviet military intervention."
Carter and Brzezinski were overjoyed that the U.S. would then have the sweet revenge of giving Russia a bloody Vietnam experience. Carter and Brzezinski gave Afghanistan a Vietnam experience too, in which millions of people were destroyed.
Once events in Afghanistan were set into motion, then Carter and Brzezinski added gasoline to the inflamed Islamic fanatics with billions of dollars in U.S. military support funneled through Pakistan. Thousands of mercenaries from Arab countries poured into Afghanistan to join the fight and get paid too. They were called "Afghan-Arabs". The Brzezinski-Carter scheme would be continued under President Reagan and led to the destruction of Afghanistan, the re-subjugation of Afghan women, and millions of Afghan civilian victims of war. Afghanistan never recovered but from its ashes rose up the Taliban and al Qaeda. The mysteries surrounding the attacks on the U.S. of September 11th 2001 are unresolved. Regardless of whodunit the attacks of 9/11 would be used as justification by President W. Bush to launch another invasion of Afghanistan.
Brzezinski's egotism to show how smart he thought he was is the smoking gun of George Orwell's famous quote:
He who controls the past controls the future. He who controls the present controls the past."
For decades the U.S. pretended that the false narrative of a Soviet invasion of Afghanistan was reality. The U.S. glorified the mujahideen as freedom fighters, when actually they were Islamic fanatics and mercenary proxies used by the U.S. and its coconspirators. The U.S. and the Saudis funded a heavily armed brand of Islamic terrorism that the U.S. though that it could control, ignore or kill off once it was no longer needed.
As Brzezinski also said in that Le Nouvel Observateur interview:
What is most important to the history of the world? The Taliban or the collapse of the Soviet empire? Some stirred-up Muslims or the liberation of Central Europe and the end of the cold war?"
The U.S. thought that it had found the "silver bullet" to destroy all of its enemies and control West Asia with "stirred-up Muslims". With the illusion of such power the U.S. fed its addiction to regime change projects using Islamic mercenaries. As Brzezinski said, controlling people is difficult. The result of regime change projects has been chaos. For the U.S. chaos is an opportunity. It is of no concern for the elites that chaos costs millions of people in the world to suffer.
Today the destruction of Afghanistan, Iraq, Libya and Syria can be laid posthumously at the feet of Brzezinski. Carter has escaped public blame and he still teaches Sunday School as the Nobel Peace Prize president. Brzezinski was so good at killing millions of people that he went on to advising another Peace Prize president how to do it. President Obama called Brzezinski "one of our most outstanding thinkers". Obama picked up the killing where W. Bush left off.
Killing millions of people instead of trying to control them is the model for asymmetrical warfare. Asymmetrical warfare is when the U.S. destroys a small country that has a limited ability to defend itself and cannot strike back. The tools of the trade are the CIA, air power, missiles, drones and mercenaries.
The U.S. public has become immune to its government killing millions of Koreans, Vietnamese, Afghans, and Arabs, as long as it does not interrupt with the public's busy daily lives. The opportunity costs to the American people is costing them universal healthcare, affordable higher education, modern mass transportation, modern infrastructure and economic security in old age; but the public does not seem to notice, and cannot connect the dots. They still think that the U.S. has the highest standard of living in the world. The reality is that the U.S. comes in at about 19th on everything from the infant mortality rate to high speed internet; except military spending and political prisoners at which the U.S. is number one.
How did the U.S. fall so low when it started with such high hopes on July 4th 1776 with the Declaration of Independence that proclaimed:
We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness"?
Those words never really meant what most Americans think they mean. American capitalism is a hierarchical based class society. Property bestows more rights and privileges on the top of the pyramid of wealth. The Founding Documents of the U.S.A. were written by the elites, for the elites and for future elites in the lottery of capitalism American-style. By "Life" is meant for those that can afford the necessities and luxuries of life.
"Liberty" is the liberty to close off the commons from the natives and those that do not own property. "Happiness" is the happiness that comes with owning slaves, and finding foreign sources of cheap labor.
As the greed for more and more land, more slaves and more wealth grew, the elites declared that it was Manifest Destiny for them to spread capitalism and the American way of life across all of North America. It was then that Asia's destiny became America's destiny. When the U.S. boundary met the Pacific Ocean the U.S. did not stop its Westward expansion. It kept on going to Hawaii, Japan, China and Korea. The U.S. joined the European scramble for overseas colonies and empire.
Capitalism must expand to survive. Adam Smith's quaint form of capitalism was based on bartering. The butcher, baker and candlestick maker traded their products with each other providing all with meat, bread and light. Modern capitalism American-style is based on money, and money is based on debt. Debt requires that interest be paid by the borrower to the lender, thus syphoning off capital from the capitalist to the financier. The capitalist must expand production and find sources of cheap labor to pay the interest and to skim a profit too. Expanding production means that more and more natural resources are needed. To produce more requires even cheaper labor and automation. To sell an ever increasing amount of finished products of capitalism requires advertising and expanding to foreign markets. If any link in the chain is broken then the Ponzi scheme of capitalism collapses into an economic depression.
Economic depressions cause social unrest, civil disturbances and class conflict. When the social hierarchy of capitalism is shaken by economic depression, then capitalism must restore order either with violent repression or appeasement by redistributing some of the wealth to reduce tension. The first instinct of the capitalists is to resort to violence and repression to preserve their wealth. That is why capitalism and true democracy cannot coexist. One or the other has to give or the system is in danger of breaking down entirely.
The natural political order for capitalism is right-wing dictatorship, martial law and fascism. Only by continuous expansion, imperialism and the exploitation of others can there be a compromise between capitalism and democracy domestically. When U.S. presidents say that the "American Way of Life" is being threatened by enemies what they mean is that small countries resist being subjugated and exploited by the U.S. Empire.
When the U.S. first began to expand beyond the shores of the Pacific Ocean they were met with rejection. Ancient societies such as in Japan, Korea and China had civilizations with thousands of years of history. They had their own social order, their way of life and they were self-sufficient. They told the U.S. capitalists to go away. Such behavior by Asians was intolerable to the U.S. capitalist and merchant classes who wanted to sell their excess production and make trading profits. The U.S. was outraged that Asians, whom the white supremacists Euro-Americans saw as being non-Christian barbarians and inferior humans, would act so superior as to refuse to buy and sell with U.S. traders.
How was the U.S. to expand trade when it was met with such 'insolence'? The answer that came naturally was to send in the navy with gunboats and the marines with bayonets to subjugate the 'insolent barbarians' to either open up to trade or have their cities bombarded with cannon balls and burnt to the ground.
The "West was won" with genocide of the Native Americans, and if genocide was needed to subjugate Asia, then the U.S. would use it, and it did. The U.S. continued its killing spree in the Philippians, Korea, Indonesia, and Southeast Asia. The U.S. considers the Pacific Ocean a U.S. lake.
The U.S. is still trying to control the world with cannon balls. The cannon balls keep getting bigger, more technologically complex and more genocidal. The U.S. cannot accept the reality that it cannot control the world. The more it tries to, the more destruction, death and chaos it causes. Instead of creating world order the U.S. keeps creating world disorder. World disorder threatens domestic order as more and more of the U.S. wealth is drained off by military expenditures and maintaining a foreign empire.
Vladimir Lenin explained how today's U.S. foreign policy works in his 1917 thesis on the causes of World War One: "Imperialism, the Highest Stage of Capitalism". Capitalism leads to monopoly, monopoly leads to imperialism and imperialism leads to war. Capitalism also leads to inequality. Inequality and democracy cannot coexist forever because there are limits to expansion and growth. To preserve inequality requires fascism and a police state. Democracy can only thrive if there is more equality. Eventually, choices will be made between fascism or democracy, and between capitalism or something else?
This article originally appeared on OpEdNews.com
David William Pear is a progressive columnist writing on economic, political and social issues. His articles have been published by OpEdNews, The Greanville Post, The Real News Network, Truth Out, Consortium News, Global Research, and many other publications. David is active in social issues relating to peace, race relations and religious freedom, homelessness and equal justice. David is a member of Veterans for Peace, Saint Pete for Peace, CodePink, and International Solidarity Movement.
If you enjoy OffG's content, please help us make our monthly fund-raising goal and keep the site alive.
For other ways to donate, including direct-transfer bank details click HERE.


text:
Create a Website Account - Manage notification subscriptions, save form progress and more.
Registration and changes to your voter registration can be made online at Massachusetts Secretary of State's Election Division, in person at the Town Clerk's Office during normal business hours, or by submitting a Mail in Voter Registration form (paper copies of this form are also available at the Lincoln Public Library). Mail-in registration forms must be post-marked by the tenth day prior to the election.
The registration deadline for first-time voters is twenty (20) days prior to the election or the Annual Town Meeting in which they wish to participate. The deadline to register to vote in a Special Town Meeting is ten (10) days prior to the Special Town Meeting.
Massachusetts law protects Voters' Rights by strictly limiting the circumstances under which an individual can be removed from the Voter List. This can result in a List which is bloated with the names of departed residents. If you are a Registered Voter of the Town of Lincoln and anticipate leaving or have already left town without informing us in writing, please do so here and now. You may download and print a postcard notice of your departure here.


text:
Projects and Services Committee Meeting
1. Call to Order
2. Meeting Open to Public
• Members of the audience may address the Committee on any matter within the general subject matter jurisdiction of the LAVTA Board of Directors.
• Members of the audience may address the Committee on items on the Agenda at the time the Chair calls for the particular Agenda item.
• Public comments should not exceed three (3) minutes.
• Agendas are published 72 hours prior to the meeting.
• No action may be taken on matters raised that are not on the Agenda.
3. Minutes of the May 20, 2013 Meeting of the P&S Committee
Recommendation: Approval
4. August 2013 Service Changes
Recommendation: Recommend the service change proposals to the Board of Directors for approval and implementation in August 2013.
5. Matters Initiated by Committee Members
6. Next Meeting Date is Scheduled for: July 22, 2013.
7. Adjourn
Please refrain from wearing scented products (perfume, cologne, after-shave, etc.) to these meetings, as there may be people in attendance susceptible to environmental illnesses.
In the event that a quorum of the entire Board is present, this Committee shall act as a Committee of the Whole. In either case, any item acted upon by the Committee or the Committee of the Whole will require consideration and action by the full Board of Directors as a prerequisite to its legal enactment.
I hereby certify that this agenda was posted 72 hours in advance of the noted meeting.
LAVTA Administrative Services Department, June 19, 2013
On request, the Livermore Amador Valley Transit Authority will provide written agenda materials in appropriate alternative formats, or disability-related modification or accommodation, including auxiliary aids or services, to enable individuals with disabilities to participate in public meetings. A written request, including name of the person, mailing address, phone number and brief description of the requested materials and preferred alternative format or auxiliary aid or service should be sent at least seven (7) days before the meeting. Requests should be sent to:
Executive Director
Livermore Amador Valley Transit Authority
1362 Rutan Court, Suite 100
Livermore, CA 94551
Fax: 925.443.1375
Email : email@example.com


text:
Hetty Lipscomb is development writer and stewardship manager at Freer|Sackler.
"Hospitality starts with a glass of water"
Artist talk with Rina Banerjee
Scientist and artist Rina Banerjee has created a site-specific work in the Sackler Pavilion as part of the Perspectives series of contemporary art, called A World Lost, referencing the major rivers of Asia. Actually, the full title is: A World Lost: after the original island, single land mass fractured, after populations migrated, after pollution revealed itself and as cultural locations once separated merged, after the splitting of Adam and Eve, Shiva and Shakti, of race black and white, of culture East and West, after animals diminished, after the seas' corals did exterminate, after this and at last imagine all water evaporated…this after Columbus found it we lost it imagine this. It's a wonderfully organic work, "growing" like a sea creature with tentacles and debris spilling all over the gallery's stone floor. For Banerjee—and for all of us—the river is a metaphor for life and what we value, and an actual source of survival for many people around the world.
An experience with her mother a few years ago spurred Banerjee to think about the importance of water and inspired the sculpture. Her mother wanted to sell some property in Bangladesh, and Banerjee traveled with her from New York to help with the transaction. After they signed various papers in the local magistrate's office, her mother wanted to to see who was living at the site. Property rights allow for squatters if the land is not occupied; whoever needs it can use it.
When they arrived, they saw a small house next to a pond. Inside the house, two little girls were digging in the dirt floor to reach the water table. Actually, one was digging, and the other was straining the water through an old cloth to make it drinkable, evidently a daily task. Tradition requires that when a visitor comes to your home, the first thing you do is offer her a glass of water. When one of the little girls spotted Banerjee and her mother, she immediately poured the water into a plastic cup and handed it to them. To be given something so valuable and so hard earned is an honor. In turn, Banerjee honors the young girls' generosity and the vital importance of water by incorporating plastic cups in the Sackler installation.
Rina Banerjee's A World Lost will be on view through June 2014.


text:
What are people saying?
"The Pearl Project honors the truth through patient, meticulous, and accurate reporting."
Robert J. Ross
>> read more
The Pearl Project is an innovative investigative journalism project at Georgetown University. The original investigation, conducted in 2007 through 2010, explored the kidnapping and murder of Wall Street Journal reporter Daniel Pearl in 2002. The 31-thousand word report was published by the Center for Public Integrity.
During the academic school year 2007-08, The Pearl Project took shape as a six-credit seminar led by Barbara Feinman Todd, then the associate dean of journalism in the School of Continuing Studies, and former Wall Street Journal reporter Asra Q. Nomani, a professor in the practice of journalism. Nomani, Pearl's friend and colleague from his days at the Wall Street Journal's Washington bureau, rented the Karachi home where Pearl and his wife Mariane were staying at the time of his disappearance. After participating in the search to find Pearl, Nomani began the preliminary reporting and research for The Pearl Project.
The project included graduate students from Georgetown's School of Foreign Service, Journalism graduate students, and Georgetown undergraduates from a variety of majors. Students in the seminar investigated the questions of who really killed Pearl and why they killed him. They also examined the wider relationship between the Muslim world and the press and profile others who have died in the frontlines of journalism. The project took place during the fall 2007 semester and the spring 2008 seminar. While the class came to a conclusion in May of 2008, a small team of students and fellows continued to work with the co-directors on the project through the fall of 2010. In the winter of 2011, their findings were published.
At a time when investigative journalism is on the wane in the media industry, the Pearl Project is modeled after the Arizona Project, an investigative reporting project into the murder of Arizona Republic reporter Don Bolles three decades ago. The Pearl Project has welcomed the participation of journalists, media organizations, media outlets and sources from around the world. While our original investigation has been published, we are always interested in any new information regarding Daniel Pearl's kidnapping and murder and can be reached at firstname.lastname@example.com or (202) 687-7136.
Since the release of the findings of the original Pearl Project, we have turned our attention to new projects and have acted as advisers to other journalism educators who are pursuing investigations at their universities. There are faculty-student investigative reporting projects emerging at universities around the world to fill the gap created by elimination of investigative reporting teams in this age of media consolidation. We remain committed to nurturing a new generation of investigative reporters trained in old school gum shoe reporting combined with new media expertise.


text:
Sesame seeds are my favourite kitchen condiment, be it white or black. I love to use them in both my cooking and baking for their flavourful aroma. Besides, their tiny size and contrasting colours give an elegant touch to any simple dish. So, for sure, any recipe with sesame seeds as an ingredient would catch my attention, just like this popular Japanese home-style dish that I learnt from Ju of Little Teochew. It calls for black sesame seeds to be toasted and grinded lightly to bring out the extra flavour. When mixed with seasonings, it forms a wonderful dressing for the blanched beans to give a light and healthy dish. Captivated by her strong recommendation and fabulous shots, I was convinced that this is a must-try recipe. She further pointed out that the recipe was a creation of "Martha Stewart of Japan" Harumi Kurihara, a celebrity homemaker and television personality in Japan. That's also how I got introduced to the delightful world of Japanese cuisine. Thanks Ju!
Since then, I became increasingly eager to learn more about Harumi's recipes. I started off by first going to the library to search for her cookbooks. I managed to borrow one among her many published titles – "Harumi's Japanese Cooking" which features plenty of easy to prepare meals. This cookbook was awarded "Best Cookbook of the Year" by Gourmand World Cookbooks Awards in 2004. The recipe I posted today is adapted from it. I gave a little tweak to the recipe to suit my liking, in which I substituted green beans with fine French beans for extra crunchiness. I also added a little bit of white sesame seeds to help me judge the stage of the toasted sesame seeds better as it's impossible to do so with black sesame seeds and avoid having burnt seeds.
I made this dish for my mother in law's to sample during her recent visit. I was a bit worried of her response as she usually avoided eating anything with sesame seeds as she found them irritating and hurtful given that they often get lodged in between her denture and gum. Being mindful of her aversion, I used food processor to grind the toasted seeds more finely before mixing them with seasonings in a bowl to get a smoother paste. I made the dressing in advance and store them in the refrigerator. I couldn't believe that she fall for this dish at her first bite. She was so impressed by how quickly I prepared this dish. Now she can't wait to try it out this recipe herself when she returns home. She totally agrees that it is a great way to incorporate seeds in her diet.
I'm so happy to find a new way of enjoying fine French beans and black sesame seeds. My son loves this sesame dressing so much that he even licked his bowl clean. All said, I felt encouraged to make this dish more often in future.
If you like this article, please share:
>>> Click on PAGE 2 BELOW for Fine French Beans with Sesame Dressing and step by step photos…………
It always amazes me how something as tiny as sesame seeds can yield so much flavor but they really do! I love the idea of using them to spruce up some green beans. Delicious.
I have 2 Harumi books but had been underusing them! I like this dish too, it's also nice eaten cold like a salad.
Hmm,I think I'd like this!
I always have sesame seeds in my pantry. Great flavours and good for granishing =)
I've been making this for years after learning of it in a small Japanese cooking class that was offered through the local Parks & Rec. Always a crowd pleaser. I usually find that when the black sesame seeds are popping like firecrackers, they're ready. I grind mine by hand in a Suribachi and usually add instant HonDashi for a yummy, tangy zip.
That's a nice simple dish! And I like that you can make the dressing in advance.
Thanks for your kind mention, LK. You made a lovely version and your shots put mine to shame!
this looks good! I'll do this!
Previously I wonder where can I get the supply of sesame seeds, but now I found it in a local supermarket. 😛
Well, I will try to make some once I got a bottle of mirin and some sesame seeds. 🙂
Love your shots on raw French beans.
Great combo, these are the two simple ingredients I loved most.
mmmm…that's simple, healthy and delicious. I love French beans. I can eat a big bowl 😀
This is fantastic, healthy and simple dish for the whole family. 🙂
What a healthy dish! I need to go buy myself a bottle of mirin soon.
This is an interesting combo…Would love to try it…:)
Simple and healthy looking, I can't resist but say yes I want!!
Interesting way of cooking the beans. 🙂
You've made a simple French beans dish looks so yummy!
Our whole family is a big fan of green beans. Love your way of dressing them with black sesame seeds, very fragrant.
I like Japanese cooking. It's good idea to add to my boy lunchbox. But I have more whites than black. Does it make the dish taste differently using white sesame?
wow! looks really great. I'm a fan of Harumi's cookbooks too. Your photos are so gorgeous like usual 🙂
Love this, I like her cookbooks too and your french beans look so yummy 🙂
We can also replace mirin with two parts cooking sake (aka rice wine) + one part sugar 🙂


text:
Fetus yawning reveals brain development
Even fetuses yawn, a new study shows.
During the second and third trimester, fetuses yawn frequently, and those yawns can be distinguished in four-dimensional ultrasounds from other mouth-opening behaviors. As the fetuses mature, they seem to yawn less frequently, the researchers added.
The findings, published Nov. 21 in the journal PLoS One, suggest yawning patterns of the fetus across pregnancy could be used to assess whether a growing fetus is developing normally.
Scientists don't know why adults, let alone babies, yawn, said study co-author Nadja Reissland, a developmental psychologist at Durham University in the United Kingdom. But yawning behavior changes throughout life. [See Images of Yawning in the Womb]
Yawns in adults are contagious, but babies don't catch them. Anemic babies yawn more in utero than healthy ones, according to a 1999 study detailed in the journal Ultrasound Obstetrics and Gynecology. And premature babies yawn more than full-term babies. (Previous research has shown that as fetuses develop, their facial expressions in the womb become more complex.)
That led Reissland to wonder whether yawning patterns could be an early marker of healthy development that could be seen even in utero.
While doctors frequently capture ultrasound images of open-mouthed fetuses, those scans couldn't distinguish yawning from reflexive mouth opening that prepares the baby to breast-feed, Reissland told LiveScience.
To do so, the team the team took three-dimensional ultrasound videos of seven female and eight male fetuses once a month, as they grew from 24 weeks to 36 weeks gestation (roughly five months to eight months into pregnancy). The babies-to-be yawned about six times an hour at 24 weeks, but yawning waned over the 12-week period and had stopped by 36 weeks. A 3D-ultrasound video produces 4D-ultrasound images, as the fourth dimension is time.
Reissland and her colleagues are unsure of fetal yawning's function, though they say it is not a sign of sleepiness. Rather, because the scans suggest yawning follows a predictable trajectory over fetal development, the behavior may be linked to brain development early in gestation, said Reissland.
"In fact, yawning might be a trigger for brain maturation," she told LiveScience. "Ultimately, it could be used in medical practice. If you see a fetus yawning a lot, there might be some problem."
Follow LiveScience on Twitter @livescience. We're also on Facebook & Google+.
- That's Incredible! 9 Brainy Baby Abilities
- Blossoming Body: 8 Odd Changes That Happen During Pregnancy
- 11 Big Fat Pregnancy Myths
Copyright 2012 LiveScience, a TechMediaNetwork company. All rights reserved. This material may not be published, broadcast, rewritten or redistributed.
for more features.


text:
I look back over the notes of my journey down into the Great Unknown, the Colorado River through the Grand Canyon, and I am struck by the number of times I mention change. By mile 80 we had passed through essentially all the major rock units of the Grand Canyon, and one can be forgiven for thinking that things might get repetitious, but it just never happened. I was now fifty miles farther downstream, and every morning, every day produced new revelations. I knew by now on Day 11 that I was having one of the most incredible experiences I would ever have in my life, and still five days remained.
|The river at Bedrock Rapid tries to mash my brother and sister-in-law against a cliff. I give them credit for carefully observing the geology.
But the canyon hides secrets, and one of them is that rocks exist that fill that void. There are in fact lots of rocks, in layers about 12,000 feet thick. We had visited them earlier in the trip, in the area between Nankoweap and Hance Rapid. They too rest on a "great unconformity" spanning 500 million years. They in turn are overlain by the Tapeats Sandstone. Because the Supergroup has been tilted, they contact the rocks above at an angle, so this contact is known as an angular unconformity.
Geotripper Images website, and that's the thing: I don't have many pictures of sills.
As we came around the bend of the river, a black cliff came into view, a layer that ran parallel to the enclosing layers of Bass Limestone and Hotauta Conglomerate (these form the base of the Grand Canyon Supergroup). It was a basaltic sill of huge proportions. The precise name of the rock is diabase, which was a shallow intrusion that was neither extrusive in texture (fine-grained) or intrusive (coarse-grained). It's a grain-size in-betweener.


text:
Ofelia Raffia Hand FanRegular price $90.00
A fan expresses itself through its own language. It rhythmically raises then falls, and then again, transmitting elegance to the hands of that woman who knows how to use it
Be chic!
About the Brand:
Ara Sunwear is a passionate and heartfelt homage to Southern Italy in all it's breathtaking beauty and splendor. With our collections we want to offer minimalist, clean-lined design accessories. Simple but more than basic. We believe often it is the limited number of details on a style that make it seem classy and timeless. Easy Pieces, Timeless Luxury. We promote a sober elegance inspired by nature and the past celebrating manufacturing, quality and sustainability.
Fashion Rooftop is committed to respecting your privacy and recognizes your need to protect the personally identifiable information that you share with us on this website. As you may know, when you visit sites on the internet, the owner of the site has the ability to collect information about you, from your name and address to your shopping preferences. Our goal is to adhere to
the principles set forth in this Privacy Policy and comply with all applicable laws that are designed to protect your privacy.
Personal information provided on the Web Site and online credit card transactions are transmitted through a secure server using Secure Socket Layering (SSL), encryption technology. When the letters "http" in the URL change to "https," the "s" indicates you are in a secure area employing SSL; also, your browser may give you a pop-up message that you're about to
enter a secure area.
FashionRooftop.com accepts Master Card, Visa, Discover and American Express and Paypal, the safer, easier way to pay without exposing your credit card or bank account number. Personal checks, money orders or cash are not accepted for online orders. FashionRooftop accepts payment via a third party provider.
Most orders will be shipped within 3-6 business days. Items are shipped directly from our brand partners location. You will receive items straight from designers, artists and artisans and support small businesses. If you order multiple items from different brands you will receive multiple shipments. Additionally, you will receive different tracking numbers. Processing time may vary per product and item location and is indicated in each product page. Orders placed on a holiday will be processed and shipped the following business day.
Shipping cost:
Shipping cost varies per item and location. Shipping costs are calculated at checkout. Some items are shipped free. Shipping is indicated in each product page. We automatically combine shipping cost total amount if you order multiple items from multiple brands. You may order multiple items from one brand for a flat delivery cost.
Average shipping cost:
USA Domestic- from $6 -10
USA to Rest of the world - $15-$25 up to 5lbs.
Eu to Eu countries -$10 -$15
Eu to Rest of the world - From $25
Order Confirmation Email
Immediately after you have successfully placed your order with FashionRooftop.com, you will receive an email that contains information regarding the details of your order. Including your order number, product details, promotional discounts, taxes, shipping charges (if applicable) and your shipping and billing address.Once orders has been shipped no revisions or cancellations to the order can be accepted.
After an order has been processed and shipped a confirmation email will be sent with the tracking number of the order.
*Every effort is made to keep stock of the inventory listed on our site, but occasionally an item is unavailable. If the
item ordered is out of stock FashionRooftop will notify you via email. Out of stock items may be shipped within a few weeks or cancelled.
Custom made items are shipped within the allotted time indicated in the product page.
Our return policy varies per brand. Please check each product page if returns are accepted or not. Some items are made upon order and are non-returnable. Others are exchange only. If no return policy is indicated on the product page, then this policy applies: Returns are accepted for items that have not been worn, marked, washed, dry cleaned, altered or damaged. All tags must be attached to the item(s) and packaging seals and wrappings intact. A receipt must accompany the return. You will need to contact us and request a return number within seven days of receiving your order, within 7 days after it was delivered to your mail box. Items returned within 7 days of receiving the merchandise can be returned for a refund. After 7 days items are not eligible to be returned. Online returns should be sent directly back to the designers and in the original packaging provided. If applicable any branded packaging such as jewelry boxes, dust bags, and tags should be included in your return. Skincare and beauty products can only be returned if they are unused, unopened and if they are re-sealable. Orders cannot be exchanged. You will need to return your original order and place a new one in the style, size or color that you need.
Upon receipt of returned merchandise refunds and exchanges will be processed within 3-5 business days of receiving the returned merchandise and after inspection of the items. FashionRooftop.com reserves the right to deny a refund if merchandise does not meet our return policy. Shipping and handling charges are generally non-refundable. Customers are responsible for return shipping cost.
Custom made and personalized items are non returnable.
FashionRooftop.com is not responsible for lost or damaged packages being returned.
*Please note: This return policy is only applicable to purchases made online at www.fashionrooftop.com or shop.fashionrooftop.com
FashionRooftop is a small business that supports small sustainable brands, some items are made upon request that is why we adhere strictly to our return policy.
Returns for no valid reasons would be charged 10% restocking fee.
If you use a parcel forwarding company, we will not be liable for any damage, defect, or loss that occurs. We will not offer refunds or replacements for orders that are reported missing or damaged that have been delivered to a parcel forwarding company. FashionRooftop.com is not responsible for lost or stolen packages.
For additional help or questions: email company @ fashionrooftop.com
contact claire burroughs perez NY email@example.com