Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Precision and Threading Locks

00:00 In the previous lesson, I showed two context managers from the standard library, one for using files, the other for scanning directories. In this lesson, I have two more context managers for you to see.

00:12 The two examples in this lesson are how to change the amount of mathematical precision in Decimal objects and locking objects in the threading library.

00:24 Floats should never be used for money. Let me show you why. Got to love that precision. This isn’t a Python thing, but a floating-point standard thing. Love it or hate it, it’s been around for a long time and decisions made back in the early days of computing are still affecting us.

00:42 Google donkey butt width and railway tracks for a likely-apocryphal tale about how long-lived standards are. Anyhow, tangents aside, Python’s got your back.

00:58 The decimal module gives you an arbitrarily precise way of doing math. This is what you should be using if you’re coding money. Let me do some math.

01:12 This is the inverse of the previous equation, but I wanted something with a long train …

01:22 and the decimal module allows you to specify the precision of calculations using the localcontext() context manager. As the name implies, it’s a context manager.

01:39 Inside of the code block, you adjust the precision by changing the value of the context variable’s .prec value.

01:53 There’s some math, and with the higher precision, you get more decimal points. Take that, rounding errors!

02:04 This next example is around threading locks, but before getting into that, you need to understand what a lock is for. The code on the screen here does not have a lock, and that’s a problem.

02:15 This code creates two threads by creating a thread object that points to the accumulate() function, passing in the index number to help visualize what is going on.

02:25 Each thread is added to a list, and then .start() is called, creating the actual parallel code. At the bottom here, I’m looping through the list of threads and calling .join() on each.

02:40 This means that the code will wait here until all the threads are done. Okay, so that’s how the threads are set up. Now let’s see what’s actually being threaded. Back up to the top … this is the .accumulate() method.

02:55 Each thread is going to be calling this function. Inside of the function, I loop five times. Each time, I’m opening a file and reading its contents. It expects the contents to be a single integer. It then increments this integer, reopens the same file, and writes the new value.

03:14 You can think of this as the world’s simplest database. The thread then waits for a little bit and then starts the next loop. Remember that there are two threads attempting to do this series of actions at the same time. Before I continue, can you anticipate what will go wrong?

03:33 Let’s try it out.

03:41 And there it is. Falls down, goes boom. There’s an exception here on the second thread. And note, even while printing out that exception, that the first thread keeps running.

03:55 The cause of the exception is thread 2 reads the file expecting an integer and instead gets an empty string. This happens because thread 1 is midway through writing a new value.

04:05 It has started writing to the file, but hasn’t finished yet. This is called a race condition, and this is why I try to avoid parallel programming unless I absolutely have to.

04:16 Let me show you what can be done about this. This is a slightly modified version of the code. In the new version, a thread lock is created. Inside of .accumulate(), the thread lock is locked using a context manager. Now, only one thread at a time can execute the body of the for loop.

04:38 If thread 1 is doing something with the file, thread 2 will have to wait until the lock is released. Using a context manager here is really good practice. Forgetting to release a lock in your code can result in two threads being blocked and never running again.

04:53 That’s called dead-locking. The use of a context manager means you can’t accidentally forget to release the lock. It’s done for you when the block is exited.

05:03 Let me just show you the new code working …

05:11 and that’s better. No more crashing. You’ve seen a bunch of context managers in practice. Next up, you’ll learn to write your own.

Become a Member to join the conversation.