Why is CFTHREAD resuing local variable values in a loop?

49 Views Asked by At

When creating threads in a loop, it appears that each thread is overwriting the previous. Here is some pseudo code:

<cfscript>
LOCAL.Items=[
    "ABC",
    "DEF",
    "GHI",
    "JKL",
    "MNO",
    "PQR",
    "STU",
    "VWX",
    "YZ"];

for (LOCAL.item in LOCAL.Items) {
    writeoutput(LOCAL.item & "<br />");
}

for (LOCAL.item in LOCAL.Items) {
    thread name="items-#LOCAL.item#" action="run" {
        writelog(text=LOCAL.item, type="information", file="thread-test");
    }
}
</cfscript>

Output of first loop:

ABC
DEF
GHI
JKL
MNO
PQR
STU
VWX
YZ

(as expected)

Data in thread-test.log:

"Information","cfthread-23","03/11/24","13:18:40","MNO"
"Information","cfthread-28","03/11/24","13:18:40","YZ"
"Information","cfthread-23","03/11/24","13:18:40","YZ"
"Information","cfthread-28","03/11/24","13:18:40","YZ"
"Information","cfthread-23","03/11/24","13:18:40","YZ"
"Information","cfthread-28","03/11/24","13:18:40","YZ"
"Information","cfthread-23","03/11/24","13:18:40","YZ"
"Information","cfthread-28","03/11/24","13:18:40","YZ"
"Information","cfthread-23","03/11/24","13:18:40","YZ"

Shouldn't naming each thread uniquely create a unique thread and use the current value of LOCAL.item?

What am I missing here?

2

There are 2 best solutions below

0
Eric Belair On BEST ANSWER

Did a little more poking around and found that cfthread/thread can accept custom attributes which can be accessed via the ATTRIBUTES scope. This seems to work:

for (LOCAL.item in LOCAL.Items) {
    thread name="items-#LOCAL.item#" action="run" item=LOCAL.item {
        writelog(text=ATTRIBUTES.item & "<br />", type="information", file="thread-test");
    }
}

Contents of thread-test.log:

"Information","cfthread-29","03/11/24","13:48:35","ABC" "Information","cfthread-31","03/11/24","13:48:35","GHI" "Information","cfthread-32","03/11/24","13:48:35","JKL" "Information","cfthread-30","03/11/24","13:48:35","DEF" "Information","cfthread-33","03/11/24","13:48:35","MNO" "Information","cfthread-33","03/11/24","13:48:35","STU" "Information","cfthread-32","03/11/24","13:48:35","YZ" "Information","cfthread-34","03/11/24","13:48:35","PQR" "Information","cfthread-30","03/11/24","13:48:35","VWX"

2
Adam Cameron On

You've fixed your issue, but did not explain what the issue was. For the sake of completeness, here we go:

Shouldn't naming each thread uniquely create a unique thread and use the current value of LOCAL.item?

What am I missing here?

LOCAL.item is a variable maintained by the main thread whereas you're using it in other "sub" threads. By its very nature, the main thread will keep running (and the loop will continue) irrespective of what's going on in any sub threads.

What you're seeing is a race condition between the main thread and the sub threads you are spawning: between the time you a) start a thread and; b) it gets to writelog(text=LOCAL.item..., the main thread has moved on, so the value of LOCAL.item is on a subsequent iteration as it was when the thread was started. I presume you are seeing this because writeLog is not a fast operation, meaning the sub threads will "lag behind" the main thread.

You've found the solution: actively pass the value you need the thread to use to the thread, via an attribute. Don't rely on the state of the main thread in your "sub" threads.