How to make an array that is thread safe?

It depends.

The documentation says that anything that does not use <suspends> is already atomic (even when it runs in parallel with other code).

Immediate expressions stick together on their own. All adjacent immediate (non-async) expressions are considered to be atomic — their code is guaranteed to run without interruption within the same update, and without preemption or context switching. It is as though such code had an automatic mutual-exclusion primitive wrapped around them.

Let’s take this code that tries to avoid duplicates:

if (not Array.Find[Value]):
    set Array += array { Value }

In Verse this not need extra safety (such as locks) that it would need in other languages.

If you do suspend, that’s no longer true:

if (not Array.Find[Value]):
    Sleep(0.0)
    set Array += array { Value }

However there is no general answer: it will depend on your code structure, and array operations you need to do.

For example, in the above scenario you could check Find again after sleeping.