| 1 |
"""This proves that MS Access is single-threaded. If you use multiple |
|---|
| 2 |
Python threads, you must synchronize them by closing their Connections |
|---|
| 3 |
in order. In the example code, because we do not explicitly close the |
|---|
| 4 |
first connection before starting the second thread, the data written |
|---|
| 5 |
in the second thread is not reflected in the dataset of the first thread. |
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
If Access allowed threads properly, the output would be: |
|---|
| 9 |
|
|---|
| 10 |
Attempt: 0 Object1 Mod1 Mod1 |
|---|
| 11 |
Attempt: 1 Object1 Mod1 Mod1 |
|---|
| 12 |
Attempt: 2 Object1 Mod1 Mod1 |
|---|
| 13 |
|
|---|
| 14 |
Instead, it's: |
|---|
| 15 |
|
|---|
| 16 |
Attempt: 0 Object1 Mod1 Object1 |
|---|
| 17 |
Attempt: 1 Object1 Mod1 Object1 |
|---|
| 18 |
Attempt: 2 Object1 Mod1 Mod1 |
|---|
| 19 |
|
|---|
| 20 |
""" |
|---|
| 21 |
|
|---|
| 22 |
import time |
|---|
| 23 |
import win32com.client |
|---|
| 24 |
import pywintypes |
|---|
| 25 |
import threading |
|---|
| 26 |
import os |
|---|
| 27 |
|
|---|
| 28 |
def get_conn(): |
|---|
| 29 |
conn = win32com.client.Dispatch(r'ADODB.Connection') |
|---|
| 30 |
conn.Open(c) |
|---|
| 31 |
return conn |
|---|
| 32 |
|
|---|
| 33 |
c = "PROVIDER=MICROSOFT.JET.OLEDB.4.0;DATA SOURCE=test.mdb;" |
|---|
| 34 |
|
|---|
| 35 |
def update_record(): |
|---|
| 36 |
conn = get_conn() |
|---|
| 37 |
conn.Execute('UPDATE test SET Name = "Mod1" WHERE ID = 1;') |
|---|
| 38 |
print_table(conn) |
|---|
| 39 |
if attempt == 1: |
|---|
| 40 |
conn.Close() |
|---|
| 41 |
|
|---|
| 42 |
def print_table(conn): |
|---|
| 43 |
res = win32com.client.Dispatch(r'ADODB.Recordset') |
|---|
| 44 |
res.Open("SELECT * FROM test;", conn) |
|---|
| 45 |
data = [] |
|---|
| 46 |
if not (res.EOF and res.BOF): |
|---|
| 47 |
data = res.GetRows() |
|---|
| 48 |
res.Close() |
|---|
| 49 |
print data[0][0], |
|---|
| 50 |
|
|---|
| 51 |
def main_thread(): |
|---|
| 52 |
print "\nAttempt: %s" % attempt, |
|---|
| 53 |
|
|---|
| 54 |
try: |
|---|
| 55 |
cat = win32com.client.Dispatch(r'ADOX.Catalog') |
|---|
| 56 |
cat.Create(c) |
|---|
| 57 |
cat.ActiveConnection.Close() |
|---|
| 58 |
except pywintypes.com_error: |
|---|
| 59 |
pass |
|---|
| 60 |
cat = None |
|---|
| 61 |
|
|---|
| 62 |
conn = get_conn() |
|---|
| 63 |
conn.Execute('CREATE TABLE test (Name VARCHAR(255), ID INTEGER);') |
|---|
| 64 |
conn.Execute('INSERT INTO test VALUES ("Object1", 1);') |
|---|
| 65 |
print_table(conn) |
|---|
| 66 |
|
|---|
| 67 |
t = threading.Thread(target=update_record) |
|---|
| 68 |
t.start() |
|---|
| 69 |
t.join() |
|---|
| 70 |
|
|---|
| 71 |
if attempt == 2: |
|---|
| 72 |
|
|---|
| 73 |
|
|---|
| 74 |
time.sleep(5) |
|---|
| 75 |
if attempt == 3: |
|---|
| 76 |
conn = get_conn() |
|---|
| 77 |
|
|---|
| 78 |
print_table(conn) |
|---|
| 79 |
conn.Close() |
|---|
| 80 |
|
|---|
| 81 |
if __name__ == '__main__': |
|---|
| 82 |
for attempt in range(4): |
|---|
| 83 |
try: |
|---|
| 84 |
main_thread() |
|---|
| 85 |
finally: |
|---|
| 86 |
try: |
|---|
| 87 |
os.remove("test.mdb") |
|---|
| 88 |
except OSError: |
|---|
| 89 |
pass |
|---|
| 90 |
|
|---|