Cant access to elements of Concurrents native containers

Im experimenting with concurrent versions of Hash & MultiHash Maps but im not been able to recover data from them, is there a specific way to do this?

maybe something like TryGetValue(key, out item)?

Thanks by the way, and sorry for my ignorance

If you inspect NativeHashMap and NativeMultiHashMap you’ll see - only adding. Read more about concurent native containers in ECS docs.

I did it but couldn’t found nothing about concurrent containers in apecific.
Could you give me the link?

https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/Documentation/index.md

yes, it was there where I used to read about ECS but didnt find anything about accessing concurrent native containers
the only thing I found was this

Could yo please be more specfic?3473598--276015--Screenshot-2018-4-25 Unity-Technologies EntityComponentSystemSamples.png

Keywords - “deterministic write access”. If you look at the example of NativeCounter (or inspect any other Concurent struct in NativeHashMap, NativeQueue, etc), you’ll see that Concurent struct has [NativeContainerIsAtomicWriteOnly] attribute, that’s mean you can’t acces read data from Concurent struct.

So how do you use data stored in that containers if you cant access directly?

For write i’m using one job, for read - other (Without Concurrent extension)

The way I solved this was by using a NativeMultiHashMap:

private void DoStuff()
{
    var myCollection = new NativeMultiHashMap< int, MyData >( 123, Allocator.Temp );

    // This job will use only key=0, like this:
    // CollectionToWriteTo.Add( 0, new MyData() );
    var myJob = new SomeJob
    {
        CollectionToWriteTo = myCollection.ToConcurrent(),
    };

    var jobHandle = myJob.Schedule( this, inputDeps );
    jobHandle.Complete();

    foreach ( var data in myCollection.GetEnumerator() )
    {
        Log( "Data changed: {0}", data );
    }

    myCollection.Dispose();
}


// Extension method to ease enumerating a NativeMultiHashMap; assumes default(TKey) if no other key is passed as a parameter
public static IEnumerable< TValue > GetEnumerator< TKey, TValue >( this NativeMultiHashMap< TKey, TValue > nativeMultiHashMap, TKey key = default( TKey ) ) 
    where TKey : struct, IEquatable<TKey>
    where TValue : struct
{
    if ( nativeMultiHashMap.TryGetFirstValue( key, out var value, out var iterator ) )
    {
        yield return value;
        while ( nativeMultiHashMap.TryGetNextValue( out value, ref iterator ) )
        {
            yield return value;
        }
    }
}

You can also make a small job, where you put the results into a NativeArray.

I do it with NativeQueue, where I use a single threaded job to iterate through it and extract all entries to a NativeArray, which I can then use concurrently in a different job. Even though it’s a single thread, it’s very fast.

I’m not sure how you should iterate through the NativeMultiHashMap in a job though, as you will need to know all the keys in advance.

(Here is my Queue → NativeArray code if anyone would be interested)

[BurstCompile]
    public struct DequeueIntoArray<T> : IJob where T : struct
    {
        public int StartIndex;
        public NativeQueue<T> InputQueue;
        [WriteOnly] public NativeArray<T> OutputArray;

        public void Execute()
        {
            int queueCount = InputQueue.Count;

            for (int i = StartIndex; i < StartIndex + queueCount; i++)
            {
                OutputArray[i] = InputQueue.Dequeue();
            }
        }
    }
1 Like

Yeah that’s why I write all values using the same key. I liked your solution, I’ll give it a go soon!

Ah, well if you don’t use the actual Key-Value relation of the hashmap, and you just need to save something from a job that you can iterate over later, then using a concurrent NativeQueue is a much better way of doing it.

1 Like