I've been trying to get multithreading to work with a shared variable, but for some reason can't make it work. I've tried multiple things, but the following code is the closest I got to what I'm trying to achieve.
So I tried to define a global variable with allocShared0 of type seq[string]. Then I created a function called myThread() which will be running in a new thread, from which I would like to access my global variable. By passing the pointer to the global variable as parameter to the myThread function, I was able to read the first value in the sequence by dereferencing the pointer it in the new thread.
However, adding a new entry to this seq and then trying to access the data from the main thread does not seem to work for me.
#import system/threads
import os
var data = cast[seq[string]](allocShared0(sizeof(seq[string])))
data.add("Test")
proc myThread(test : ptr seq[string]) {.thread.} =
echo "Start of new thread"
echo test[][0]
test[].add("I want to access this from the main thread!")
echo "End of new thread"
echo "Start"
echo type(data.addr)
var thread: Thread[ptr seq[string]]
createThread[ptr seq[string]](thread,myThread,data.addr)
joinThreads(thread)
echo "Thread done. Trying to access second string from seq:"
sleep(500)
echo data.addr[][1]
PS: I also have another small issue in VSCode which shows errors on the three lines with Thread, createThread and joinThreads when I do not use import system/threads. However, when compiling without import system/threads, I get no errors. Additionally, when I do add import system/threads, VSCode doesn't show any errors, but when compiling it I get the following error: D:\Program Files\nim-1.6.6\lib\system\threads.nim(48, 10) Error: You must not import this module explicitly Not really blocking, but kinda annoying to permanently have 12 errors in my code which are not there... E.g.:
var data = cast[seq[string]](allocShared0(sizeof(seq[string])))
data.add("Test")
You cannot do that, you are casting unmanaged memory, to GC-ed memory, this will end with a crash in the GC. Though here you have a global variable so the memory is never reclaimed.
Instead compile with --gc:arc or --gc:orc so that all threads can access that sequence.
proc myThread(test : ptr seq[string]) {.thread.} =
echo "Start of new thread"
echo test[][0]
test[].add("I want to access this from the main thread!")
echo "End of new thread"
Use locks to access shared memory
Well, I've tried adding locks, but I removed them to keep it simple in the example. This is one of my other attempts, but this still gives errors: 'myThread' is not GC-safe as it accesses 'data' which is a global using GC'ed memory
import os
import locks
var myLock: Lock
initLock(myLock)
var data {.guard: myLock.} : seq[string]
data.add("Test")
proc myThread() {.thread.} =
echo "Start of new thread"
withLock myLock:
echo data[0]
data.add("I want to access this from the main thread!")
echo "End of new thread"
echo "Start"
echo type(data.addr)
var thread: Thread[void]
createThread[void](thread, myThread)
joinThreads(thread)
echo "Thread done. Trying to access second string from seq:"
sleep(500)
echo data[1]
Compiled with the following args: nim c -d:mingw --cpu:amd64 --threads:on --gc:arc --tlsEmulation:on .\test.nim
I was able to make it work by adding {.cast(gcsafe).}:, instructing the compiler that this block is GCSafe, so it won't bother checking.
import os
import std/tables
import threadpool
import locks
var myLock: Lock
initLock(myLock)
var data {.guard: myLock.} : seq[string]
data.add("Test")
proc myThread() {.thread.} =
{.cast(gcsafe).}:
echo "Start of new thread"
withLock myLock:
echo data[0]
data.add("I want to access this from the main thread!")
echo "End of new thread"
echo "Start"
echo type(data.addr)
var thread: Thread[void]
createThread[void](thread, myThread)
joinThreads(thread)
echo "Thread done. Trying to access second string from seq:"
sleep(500)
withLock myLock:
echo data[1]