We discussed the solution to the first homework. Points to take away
from the discussion:
- Assume nothing is atomic except what is atomic!
- Must always consider what could happen in machine code (i.e.,
tally += 1 isn't atomic because the intermediate result will certainly
be stored in a register).
- Difference between global shared vars (n, tally) and local var
(count). Must protect globals with lock. Don't protect locals.
Common wrong answers:
- 0 : argument given: "the
main thread creates the two threads and exists. two newly created
threads don't get a chance to execute". This is an incorrect statement.
When the main threads creates a thread, this new threads gets added to
the queue of threads that are ready to run. As stated in the project
handout, the thread library exists when there are no runnable threads
in the queue. When the main thread exists, there are still two
threads in the queue and are ready to be executed...
- 40: argument given: "If
the assignment and addition operators are atomic, then the statement
tally = tally + 1; is also atomic. Since both threads have to perform
20 iterations each, the value of "tally" can not be lower than 40".
This is an incorrect statement. Having two operations atomic doesn't
make a composition of these operations atomic.
- 1: argument given: Thread
1 performs the addition 0 + 1. Thread 2 then executes through to
completion. Thread 1 then performs the assignment tally = 1. This
is incorrect because Thread 2 has to complete 20 iteration and in this
example it only completed 1. Since "count" is a local variable to each
thread, Thread 1 's value of "count" is not
effected by Thread 2's value of "count".
- 20: (most common
answer): demostrates how a context switch between the addition and
assignment operations leads to "tally" being incremented only by 1
instaed of 2 on each iteration. But we can do better.
We discussed the right solution during the discussion. If you have
questions about the solution, come to the office hours.
Then, we discussed how to start the first project.
- Read the project handout and divide tasks between group
members.
- Make sure the output is correct: eg. spelling, format ``I
mispelled a requestor'' got 0 on my 1st submission. It's the easiest
way to waste an autograder submission for that day!
- Take a multi-threaded example program from the project handout
and run it.
- Start by creating a ``main'' function for the requestor
threads. In each thread, implement reading from the input file.
- Implement a part of the scheduler that finds the closest track.
Given an old value and a queue of request, find the next request. Print
it and remove it from the list.
- Identify shared variables. Global read-only values: eg, maximum
queue size. Global written values: eg, number of requestors, queue.
Very simple pseudo_code:
scheduler { /* "main"
function for the disk scheduler thread */
waits_for_work();
does_work();
}
requestor { /* "main"
function for a requestor thread */
submits_work();
waits_for_completion();
}
start {
create_scheduler();
create_requestor();
}
In thread library.
- Start by testing error conditions for each of the routines. For
example, in thread_lock
check that the input is within bounds.
- Implement library initialization condition: has to be called
exactly once.
- As stated by the project handout, first implement thread_libinit, thread_create, thread_yield.