B4J Question How to manage concurrent acces to a map

marcick

Well-Known Member
Licensed User
Longtime User
Hi all,
in my B4J server app I have a map containing data that can be modified from 3 different proceesses. One in a timer in the main thread and other in Server.Handler and WebSocket.
The code is something like this stupid sample:

B4X:
x=mymap.get(credits)
x=x+5
mymap.put(credits,x)

I wonder if 2 events may occour in the same moment and create errors.
Is it a good solution to manage a global flag "MapBusy" like this ?

B4X:
Do while MapBusy is true
Loop
MapBusy=true
..... works with the map
MapBusy=false
 

agraham

Expert
Licensed User
Longtime User
I wonder if 2 events may occour in the same moment and create errors.
Yes! :eek:
Is it a good solution to manage a global flag "MapBusy" like this ?
No, definitely not! :( @LucaMs suggestion is best in this case but in the absence of that you could make things thread safe by using a Lock object from my Threading library. The reason your code is not thread safe is that testing and setting the flag in code is not an atomic (uninterruptible) operation and cannot be made so. This means that your thread can test the flag, see it False and then be suspended to let another thread run before setting the flag True. Another thread can then see the flag is False and also assume it can access the map so potentially causing problems.

Threading problems are easy to create but notoriously difficult to debug so asynchronous code needs to be very carefully designed in the first case or tragedy may ensue. o_O
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
This, of course, does not protect by access from the timer event.
Yes it should. It should limit access to a single thread at a time regardless of which thread it is. The Map is based on the Java ConcurrentHashMap.

 
Upvote 0

marcick

Well-Known Member
Licensed User
Longtime User
Thank you guys, I have read that article but I'm still confused:
I create a Public ThreadSafeMap in the main code and then ?
How to lock the map between a get and a put ?
How can the second thread know that I have finished or I'm still working with the map ?
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
How to lock the map between a get and a put ?
You don't, the Map ensures that get and put are atomic operations. In a normal Map they are not necessarily atomic as they both deal with locating or adding a key then dealing with a value. Note that if you do a put then a get you are not guaranteed that you get back the same value as you put as another thread might have changed it.

Also a ThreadSafeMap enables safe iteration of the values or keys by means of the Values and Keys methods in the map without another thread's actions changing the Map in mid iteration. What you get is a list of the key and values at the time you made the call. Note again that another thread can add or remove keys and values without your knowledge.

The corollary of the above two paragraphs is that you still have to code in the knowledge that other threads might alter the map, but that they will do it in a thread safe manner.
 
Upvote 0

marcick

Well-Known Member
Licensed User
Longtime User
But when you do like my sample it is not a single instruction that can be queued.
i don ‘t understand….

x=mymap.get(credits)
x=x+5
mymap.put(credits,x)
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
So my code above is not safe anyway, correct ?
Assuming another thread might also change 'credits' then yes, it is unsafe. You need to lock a shared resource if you are performing non-atomic operations on it. For example if you are incrementing or decrementing a variable accessed from different threads you would need to use a Lock to make that thread safe.
B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Dim Lock1 As Lock 'from Threading library
    Lock1.Initialize(False) ' unlocked
End Sub

Sub ChangeCredits ' both/all threads must do this
   Lock1.Wait 'wait for unlock and then claim lock as an atomic operation
   x=mymap.get(credits)
   x=x+5
   mymap.put(credits,x) 'shared mymap declared somewhere
   Lock1.Unlock ' unlock the lock to free access to other threads using Lock1
End Sub
However you only need to do this if two different threads are going to alter the same key:value pair in the map. One updating it and the other just reading it would usually be safe.
 
Upvote 0
Top