The pointer to a block of memory that holds all of your "global" data is the best way to work with a single LONG parameter that you can pass to your thread.
However, one word of advice that will save you a lot of time debugging very complex and seemingly random GPF's:
If you are going to modify any GLOBAL variable across multiple threads then you MUST (earn how to) use synchronization objects - the rule is simple: if the variable is global across threads (or the data it points to is global across threads), use a critical section when changing any part of the variable or target memory. Failure to do so will result in GPF's when Windows forces a context-switch when data is only _partially_ updated - in effect the data is "undefined" - until the context switches back to finish the memory update. If another thread accesses this data while it is "undefined", you are asking for trouble.
This is too long to describe here, but "Win32 Programming" discusses this in-depth - do yourself a favor and learn this stuff - ISBN's are in the FAQ forum.
Threading is powerful - but it requires careful app design and implementation.
------------------
Lance
PowerBASIC Support
mailto:[email protected][email protected]</A>
Announcement
Collapse
No announcement yet.
DLL question
Collapse
X
-
Guest repliedBefore I start confusing you even more, I suggest reading the walkthrough/tutorial about threads that is in the "Tech Notes" section of PowerBasic's website.
For starters, we create new threads because we do not want to wait around for the function to return. So that means "g_Result" is only the HANDLE to the newly created thread.
The result wont appear till that thread is done. You have to program as if that thread may return instantly or take 100 years. It's what makes multithreaded programming so much fun.
There are two ways of finding out when the thread is done. We can sit & stare at THREAD STATUS till it changes from 259. Or we can let the thread post a message to a window we have open or trigger an event object. It's your choice.
Remember this: Globals live in the heap. Local variables live in the stack.
Windows only lets you pass a single LONG integer to the newly created function. Until the THREAD CLOSE, the function's return value is waiting for you at THREAD STATUS. Again, this can only be a single long integer. The best way to do this is to use a User Defined Type as dorian suggested, but instead of creating it as a global, we create it locally on the stack. Then before we create the thread, we move the UDT onto the heap and get a pointer aimed at the UDT on the heap. We then create the thread and pass the pointer. The thread creates a local UDT in its stack. Then it copies the UDT from the pointer location to the local UDT. Modifies the local UDT, and copies the local UDT up into the UDT on the heap. This sounds worse than it really is.
Code:#INCLUDE "WIN32API.INC" TYPE ThreadInfoType ' also known as a UDT ID AS LONG X AS LONG St AS ASCIIZ * 60 END TYPE FUNCTION One(BYVAL pThreadInfo AS DWORD) AS LONG DIM lThreadInfo AS ThreadInfoType DIM lpThreadInfo AS ThreadInfoType PTR lpThreadInfo = pThreadInfo ' aim at the passed heap location lThreadInfo = @lpThreadInfo ' its better to copy into local stack for editing lThreadInfo.St = MID$(lThreadInfo.St,lThreadInfo.X,4) ' modify @lpThreadInfo = lThreadInfo ' Copy back into heap FUNCTION = 1 ' All local variables will be lost. Only the heap & this function return will survive. END FUNCTION FUNCTION PBMAIN() AS LONG LOCAL hThread AS LONG, r AS LONG LOCAL TestTI AS ThreadInfoType ' a UDT containing important info for our thread LOCAL pTestTI AS ThreadInfoType PTR ' a UDT pointer TestTI.ID = 1024 TestTI.St = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" TestTI.X = 5 pTestTI = HeapAlloc(GetProcessHeap,0,LEN(TestTI)) @pTestTI = TestTI ' the @ sign means to use the UDT that the pointer is aiming at THREAD CREATE One(pTestTI) TO hThread DO THREAD STATUS hThread TO r ' r will get the FUNCTION return value LOOP UNTIL r = 1 ' this is a bad way to wait for a thread to finish THREAD CLOSE hThread TO r ' cleanup TestTI = @pTestTI ' the heap version needs to be copied back first HeapFree GetProcessHeap, 0, pTestTI ' dont forget to clean up! MSGBOX TestTI.ST END FUNCTION
1. variable PTRs are DWORDS with a special @ power
2. CREATE THREAD passes variables by value. either write your thread functions with byval or use varptr() when passing pointers around.
3. Dynamic strings & arrays are not supported in PB's UDTs. It's probably for the better when you get in situations like this. It requires quite a mess of extra copying in/out of heap and its just best to avoid them till you get the hang of this.
------------------
Leave a comment:
-
Guest repliedHi there,
I've recently done a multithreaded DLL which needed multiple paramaters. What I did was to export a function which had all the params, assign these params to global variables, and then call the threaded function. The threaded function then reads the global variables, and does its processing.
I found the best way was to declare a UDT with enough elements to account for all the parameters, populate it, and then let the called thread use it, e.g.:
TYPE UDT1
Param1 as string
Param2 as string
END TYPE
GLOBAL uMyUDT AS UDT1
FUNCTION F1 (Param1 as string, Param2 as string) EXPORT AS LONG
uMyUDT.Param1 = Param1
uMyUDT.Param2 = Param2
THREAD CREATE InitThread(id) to lRet
END FUNCTION
FUNCTION InitThread(id as LONG) as LONG
MSGBOX uMyUDT.Param1
MSGBOX uMyUDT.Param2
END FUNCTION
Note that the exported function will create the thread, and exit immediately, without waiting for the threaded function to finish.
To wait for it, I use the THREAD STATUS command (there's an example in the help) to wait around until the thread finishes. The thread could put its result into another element of the UDT, and the function F1 then returns this.
Using arrayed UDT's allows you to sporn multiple threads, and then wait until all have finished, then return the collective results.
Dorian.
------------------
Leave a comment:
-
DLL question
If I have ONE function in my DLL that uses say 5 others, and does a Thread Create InitThread(id) To g_Result for example, I can't pass parameters to it.
How do I make global variables in the DLL from the one function?
I've BEEN doing it like this:
Function One(Param as String) Export As Long
Thread Create InitThread(id) To g_Result
Function = g_Result
End Function
OK That works great, but what if I need to do this with a parameter between the functions?
Thread create only allows one long integer parameter...
Do I need to dupliate my values?
Ie, in teh DLL
DLL_St as string
Function One(St as String) Export as long
Thread Create InitThread(id) To g_Result
Function = g_Result
End Function
Function InitThread(ByVal x As Long) Export As Long
St = Mid$(St,1,4) 'or whatever
End Function
Is this St the same St that will be returned to the original function?
I'm slightly confused as to how the variables interact, my ASSUMPTION is that this is a new value of St and I will get a compile error if i do not declare it in the Thread function
Scott
-------------
Scott
mailto:[email protected][email protected]</A>Tags: None
Leave a comment: