Is there a more efficient threading lock?

Jon Ribbens jon+usenet at unequivocal.eu
Sun Feb 26 11:09:44 EST 2023


On 2023-02-26, Chris Angelico <rosuav at gmail.com> wrote:
> On Sun, 26 Feb 2023 at 16:16, Jon Ribbens via Python-list
><python-list at python.org> wrote:
>> On 2023-02-25, Paul Rubin <no.email at nospam.invalid> wrote:
>> > The GIL is an evil thing, but it has been around for so long that most
>> > of us have gotten used to it, and some user code actually relies on it.
>> > For example, with the GIL in place, a statement like "x += 1" is always
>> > atomic, I believe.  But, I think it is better to not have any shared
>> > mutables regardless.
>>
>> I think it is the case that x += 1 is atomic but foo.x += 1 is not.
>> Any replacement for the GIL would have to keep the former at least,
>> plus the fact that you can do hundreds of things like list.append(foo)
>> which are all effectively atomic.
>
> The GIL is most assuredly *not* an evil thing. If you think it's so
> evil, go ahead and remove it, because we'll clearly be better off
> without it, right?

If you say so. I said nothing whatsoever about the GIL being evil.

> As it turns out, most GIL-removal attempts have had a fairly nasty
> negative effect on performance. The GIL is a huge performance boost.
>
> As to what is atomic and what is not... it's complicated, as always.
> Suppose that x (or foo.x) is a custom type:

Yes, sure, you can make x += 1 not work even single-threaded if you
make custom types which override basic operations. I'm talking about
when you're dealing with simple atomic built-in types such as integers.

> Here's the equivalent with just incrementing a global:
>
>>>> def thrd():
> ...     x += 1
> ...
>>>> dis.dis(thrd)
>   1           0 RESUME                   0
>
>   2           2 LOAD_FAST_CHECK          0 (x)
>               4 LOAD_CONST               1 (1)
>               6 BINARY_OP               13 (+=)
>              10 STORE_FAST               0 (x)
>              12 LOAD_CONST               0 (None)
>              14 RETURN_VALUE
>>>>
>
> The exact same sequence: load, add, store. Still not atomic.

And yet, it appears that *something* changed between Python 2
and Python 3 such that it *is* atomic:

    import sys, threading
    class Foo:
        x = 0
    foo = Foo()
    y = 0
    def thrd():
        global y
        for _ in range(10000):
            foo.x += 1
            y += 1
    threads = [threading.Thread(target=thrd) for _ in range(50)]
    for t in threads: t.start()
    for t in threads: t.join()
    print(sys.version)
    print(foo.x, y)

2.7.5 (default, Jun 28 2022, 15:30:04)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
(64489, 59854)

3.8.10 (default, Nov 14 2022, 12:59:47)
[GCC 9.4.0]
500000 500000



More information about the Python-list mailing list