16

I want to use multiprocessing.Value to use a variable in multiple processes, but the syntax is not clear on Python's documentation. Can anyone tell me what should I use as type (my variable is a letter), and where to put my variable's name ?

EDIT

I tried using the Manager to share my letter between processes. But the only thing I have now is Value('ctypes.c_char_p', '(The key you hit here)') printed in the Python Shell and still no sound. The console also seems a bit slower than usual when using the manager. There's an almost one second delay between the time I hit the key and when the Value appears on screen.

My code now looks like this :

#Import 
from tkinter import * 
import wave 
import winsound 
import multiprocessing 

#Functions 

def key(event):

     temp = event.char
     manager = multiprocessing.Manager()
     manager.Value(ctypes.c_char_p, temp)
     hitkey = manager.Value(ctypes.c_char_p, temp)
     instance = multiprocessing.Process(target=player, args=(hitkey,)) 
     instance.start()



def player(hitkey):
     print(hitkey + "1")
     winsound.PlaySound(hitkey + '.wav', winsound.SND_FILENAME|winsound.SND_NOWAIT|winsound.SND_ASYNC) 


if __name__ == "__main__":



     #Initialisation 
     fenetre = Tk() 
     frame = Frame(fenetre, width=200, height=100)
     #TK

     frame.focus_set()
     frame.bind("<Key>", key)
     frame.pack()
     fenetre.mainloop()
Pollux
  • 349
  • 1
  • 3
  • 7
  • I'm confused by the section of your code you've labeled `initialization`. A `global` statement at the top level of a module doesn't do anything. It's completely meaningless. What do you intend for `event` and `hitkey` to be there? – Blckknght May 11 '13 at 14:15
  • This is an old statement I forgot to remove. I don't think it will change anything. – Pollux May 11 '13 at 14:25
  • Do you really need multiprocessing here? You certainly don't need a `Manager` or a `value` just to pass an argument to a new process (just let `multiprocessing` pass the string using `pickle`). Does your `player` function work if you simply call it from regular code, rather than in a separate process? I don't know `winsound` at all, so I can't help too much in debugging it. – Blckknght May 15 '13 at 20:23
  • @Blckknght I need to play multiple sounds at once, and Python's GIL prevents it. That's why I need multiprocessing. – Pollux May 15 '13 at 21:15
  • Does the `player` function work if you test in a single process with the same arguments? – Blckknght May 15 '13 at 22:10
  • @Blckknght Yes, but the sounds just stop each other when playing. – Pollux May 15 '13 at 22:15

2 Answers2

17

There is no special syntax for multiprocessing.Value, it's just a class like any other. The signature of the Value constructor is perfectly well described:

multiprocessing.Value(typecode_or_type, *args[, lock])

Return a ctypes object allocated from shared memory. By default the return value is actually a synchronized wrapper for the object.

typecode_or_type determines the type of the returned object: it is either a ctypes type or a one character typecode of the kind used by the array module. *args is passed on to the constructor for the type.

If lock is True (the default) then a new lock object is created to synchronize access to the value. If lock is a Lock or RLock object then that will be used to synchronize access to the value. If lock is False then access to the returned object will not be automatically protected by a lock, so it will not necessarily be “process-safe”.

You even have some examples of its use afterwards. In particolar the typecode_or_type can be one of the typecodes that are listed in the documentation for the array module(e.g. 'i' for signed integer, 'f' for float etc.) or a ctypes type, like ctypes.c_int etc.

If you want to have a Value containing a single letter you can do:

>>> import multiprocessing as mp
>>> letter = mp.Value('c', 'A')
>>> letter
<Synchronized wrapper for c_char('A')>
>>> letter.value
'A'

Update

The problem with your code is that the typecode 'c' means character not string. If you want to hold a string you can use the type ctypes.c_char_p:

>>> import multiprocessing as mp
>>> import ctypes
>>> v = mp.Value('c', "Hello, World!")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/multiprocessing/__init__.py", line 253, in Value
    return Value(typecode_or_type, *args, **kwds)
  File "/usr/lib/python2.7/multiprocessing/sharedctypes.py", line 99, in Value
    obj = RawValue(typecode_or_type, *args)
  File "/usr/lib/python2.7/multiprocessing/sharedctypes.py", line 73, in RawValue
    obj.__init__(*args)
TypeError: one character string expected
>>> v = mp.Value(ctypes.c_char_p, "Hello, World!")
>>> v
<Synchronized wrapper for c_char_p(166841564)>
>>> v.value
'Hello, World!'

For Python 3, use c_wchar_p instead of c_char_p

Abdulrahman Bres
  • 2,603
  • 1
  • 20
  • 39
Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • Tried it, but then I've got an error in my multiprocessing line of code : `Exception in Tkinter callback Traceback (most recent call last): File "C:\Program Files (x86)\Python\lib\tkinter\__init__.py", line 1399, in __call__ return self.func(*args) File "G:\Projet final\Test audio\test4-2.py", line 16, in key hitkey = multiprocessing.Value('c', temp, False)` – Pollux May 10 '13 at 15:00
  • `File "C:\Program Files (x86)\Python\lib\multiprocessing\__init__.py", line 252, in Value return Value(typecode_or_type, *args, **kwds) File "C:\Program Files (x86)\Python\lib\multiprocessing\sharedctypes.py", line 96, in Value obj = RawValue(typecode_or_type, *args) File "C:\Program Files (x86)\Python\lib\multiprocessing\sharedctypes.py", line 73, in RawValue obj.__init__(*args) TypeError: __init__ expected at most 1 arguments, got 2` – Pollux May 10 '13 at 15:00
  • @FalafHellFalafHell You are doing something wrong. *Show us the code!* – Bakuriu May 10 '13 at 15:06
  • Here's the multiprocessing function : `def key(event): temp = event.char hitkey = multiprocessing.Value('c', temp, False) instance = multiprocessing.Process(target=player, args=(hitkey.Value,)) instance.start()` – Pollux May 10 '13 at 15:10
  • @FalafHellFalafHell Please edit your question and add code there. Also, instead of posting a small piece of the code that we are not able to run it's much more helpful if you can provide us with an entire program that reproduces the behaviour and that we are able to run on our computers. – Bakuriu May 10 '13 at 15:14
  • @FalafHellFalafHell Can you edit your question and write the full traceback you are now getting? Anyway my guess is that you are trying to use `Value` to store some complex object, in which case you really should use the `Manager` class(see [this](http://stackoverflow.com/questions/3671666/sharing-a-complex-object-between-python-processes) related question). – Bakuriu May 11 '13 at 06:26
  • Edited, I'm afraid proxy bojects will be a little bit too complicated, will try it though. – Pollux May 11 '13 at 14:03
  • 1
    @FalafHellFalafHell The argument for the type of `Value` is not the string `'ctypes.c_char_p'`. Is the *object* `c_char_p` from the module `ctypes`. Read carefully the last example in my answer. – Bakuriu May 12 '13 at 06:37
  • 1
    I have found `v = mp.Value(ctypes.c_char_p, "Hello, World!")` to be problematic, leading to segmentation violation, because, I surmise, the item in shared memory is a pointer to a string, not the string itself. – jjc Jan 27 '17 at 22:57
2

I think the original issue you were having (causing the TypeError) was because the lock argument to the multiprocessing.Value constructor is a keyword-only argument. You need to call multiprocessing.Value("c", temp, lock=False) in order for it to do what you were wanting to do.

However, I don't think you need to use a Value object at all. You're passing the keycode as an argument to your other process, and the Value isn't being used at all. I'd get rid of it completely:

def key(event): 
   instance = multiprocessing.Process(target=player, args=(event.char,)) 
   instance.start()
Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • I tried `hitkey = multiprocessing.Value('c', temp, lock=False)`, I have a `TypeError: one character string expected` error. I also tried without the `Value` object, and again, no sound playing. – Pollux May 11 '13 at 15:54
  • As I already pointed out in my answer the problem is that `'c'` expects a *single* character, while Falaf is passing in a string. Using a `ctypes.c_char_p` works. – Bakuriu May 11 '13 at 16:54
  • Right, I forgot. Fixed it, but there's still this `TypeError: this type has no size`. – Pollux May 11 '13 at 19:51
  • 1
    @FalafHellFalafHell: Have you tried the version without the `Value`? It should work regardless of what `event.char` is. It will work for any string. Of course, if the string is not what you expect, the other process may not find the `wav` file you're wanting it to play. You might want to print out `event.char` for debugging, to make sure it is what you expect. – Blckknght May 12 '13 at 01:36
  • @Blckknght Yes, I tried with `instance = multiprocessing.Process(target=player, args=(event.char,))` and `print(event.char)`,but the only thing I get is the letter printed, no sound. – Pollux May 12 '13 at 11:52
  • @FalafHellFalafHell It looks like the `TypeError: one character string expected` was due to passing in a Unicode string (which is the default string type in Python 3). It works if you pass a `bytes` instance with a single character, and really it seems like a bug that it fails for ASCII characters in a unicode string (a bug in the error text at least). But I really don't understand why a `Value` or `Manager` is required here, since the value isn't going to be updated while the other process is running. Just pass the value directly, as I have in my example should work just as well. – Blckknght May 15 '13 at 20:29
  • @Blckknght I know, it should work, but it just doesn't. I tried with your exemple several times, and nothing happens. – Pollux May 15 '13 at 21:17