It looks from my testing like the class properties are contained within each thread, but that the public variables are shared between the two threads.
Anyone have experience with this? And...is there any way to suppress this behavior, so that public variables are not shared between threads? Although it seems that could be really useful, I'm working with a large legacy app strewn with public variables that must retain their values in their own threads (and I'd really don't want to rewrite it to change the publics to properties).
Are you using the vfpt.dll runtimes? They should not share global vars although some VFP system vars are in fact shared.
+++ Rick ---
The registry entry has the setting Foxruntime = VFP9T.DLL, so I assume yes.
Here's my code. In the real-life scenario test1 is the legacy app and test2 is an olepublic adapter dll.
Test1
return createobject("test1") define class test1 as Custom inCounter = 0 function AddToCounter lparameters tnNum this.inCounter = this.inCounter + tnNum endfunc enddefine
Test2
Compiled as SingleUse or MultiUse yields the same results.
Compiled a Multi-Threaded COM Server (dll)
When run, Test2.inCounter is within each thread (counts to 10) and goTest1.inCounter is shared (counts to 20).
The commented code has test1 as a property of test2 instead of a global variable, and counts to 10 instead of 20.
define class test2 as Session olepublic inCounter = 0 * ioTest1 = null function init() as Boolean public goTest1 goTest1 = test1() * this.ioTest1 = test1() endfunc function AddToCounter(tnNum as Integer) as String local lcReturn this.inCounter = this.inCounter + tnNum goTest1.AddToCounter(tnNum) lcReturn = transform(goTest1.inCounter) + "," + transform(this.inCounter) * this.ioTest1.AddToCounter(tnNum) * lcReturn = transform(this.ioTest1.inCounter) + "," + transform(this.inCounter) return lcReturn endfunc enddefine
.NET Test App
I used tblimp to create the .Net wrapper: tlbimp test2.dll /out:Test2RCW.dll
using System; using System.Collections.Generic; using System.Threading; using Test2RCW; namespace tester { class Program { static void Main(string[] args) { Console.WriteLine(String.Format("Main: Threading Model = {0}", Thread.CurrentThread.GetApartmentState())); // Run outside static main Program p = new Program(); p.Tester(); Console.WriteLine("Press [ENTER] to exit..."); Console.ReadLine(); } public void Tester() { Console.WriteLine(String.Format("Tester: Threading Model = {0}", Thread.CurrentThread.GetApartmentState())); Thread t1 = new Thread(Worker); Thread t2 = new Thread(Worker); t1.Name = "1"; t2.Name = "2"; t1.Start(); t2.Start(); } public void Worker() { Console.WriteLine(String.Format("Thread {0}: Instantiate Test2 - Threading Model = {1}", Thread.CurrentThread.Name, Thread.CurrentThread.GetApartmentState())); var test2 = new Test2RCW.test2(); Console.WriteLine(String.Format("Thread {0}: Beginning Loop", Thread.CurrentThread.Name)); for (var i = 0; i < 10; i++) { var result = ParseNums(test2.AddToCounter(1)); Console.WriteLine(String.Format("Thread {0}: Test1.inCounter = {1}, Test2.inCoutnter = {2}", Thread.CurrentThread.Name, result[0], result[1])); Thread.Sleep(50); } } public List<int> ParseNums(string counters) { var cnums = counters.Split(new char[] { ',' }); var result = new List<int>(); for (var i = 0; i < 2; i++) { result.Add(int.Parse(cnums[i])); } return result; } } }