InvalidOperationException: Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.
InvalidOperationException: 비동시 컬렉션을 변경하는 작업에는 독점 액세스 권한이 있어야 합니다. 이 컬렉션에 대한 동시 업데이트가 수행되어 컬렉션의 상태가 손상되었습니다. 컬렉션의 상태가 더 이상 올바르지 않습니다.
서로 다른 스레드에서 딕셔너리에 동시 접근하면 해당 에러가 발생한다.
해결 시도 :
다른 스레드에서 작동하는 코드를 다음과 같이 작성하였다.
try { connectDict[port] = (tcp, stream); }
catch (InvalidOperationException)
{
while (!connectDict.ContainsKey(port))
{
Debug.Log(port);
connectDict[port] = (tcp, stream);
}
}
그리고 테스트 버튼을 만들어 딕셔너리를 확인 해보았다.
foreach (KeyValuePair<int, (TcpClient, NetworkStream)> pair in connectDict)
Debug.Log($"{pair.Key} : {pair.Value.Item1 == null}, {pair.Value.Item2 == null}");
하지만 안타깝게도 하나의 pair를 제외한 나머지는 0 : false, false 가 나왔다. 딕셔너리가 손상되어 버린것.
특이하게도 catch문의 while문이 작동을 안함. 손상전에 try-catch문이 작동하는 것 같음.
해결법 1: 멤버변수를 이용하여 순차적으로 처리해줌.
나의 경우 멤버변수인 _port를 이용하여 해결
private void ServerConnection(string ip, int port, Action<TcpClient, NetworkStream> serverConnection)
{
TcpClient tcp = null;
NetworkStream stream = null;
tcp = new TcpClient(ip, port);
stream = tcp.GetStream();
while (!(port - _port == connectDict.Count)) Debug.Log("Wait");
connectDict[port] = (tcp, stream);
serverConnection(tcp, stream);
}
하지만 이렇게 되면 기존의 코드의 규칙이 바뀌면 이걸 또 바꿔줘야함
해결법 2:
기존의 딕셔너리보다는 속도가 떨어지지만 다중 스레드에서도 안정성 보장된다.
ConcurrentDictionary<TKey,TValue>
ConcurrentDictionary<TKey,TValue> 클래스 (System.Collections.Concurrent)
여러 개의 스레드에서 동시에 액세스할 수 있는 키/값 쌍의 스레드로부터 안전한 컬렉션을 나타냅니다.
learn.microsoft.com
InvalidOperationException: Collection was modified; enumeration operation may not execute.
이 에러는 딕셔너리와 같은 컬렉션을 foreach문을 통해 작업 중 딕셔너리가 수정되면 발생되는 에러이다.
이것또한 보통 다른 스레드에서 동시작업할 때 발생되는 에러이다.
나의 경우, while문에서 foreach문을 통해 딕셔너리를 읽고 있는 중에, 다른 스레드에서 단발적인 수정이 이루어지므로 try-catch문을 통해 해결하였다.
try
{
foreach (int num in dict.Keys)
{
if (list.Contains(num)) continue;
if (myClientNum == num) continue;
Transform newPlayer = Instantiate(playerPrefab, playGround);
newPlayer.name = num.ToString();
playersDict[num] = newPlayer.GetChild(0);
list.Add(num);
}
//나간 유저 있는지
foreach (int ls in list)
{
if (dict.ContainsKey(ls)) continue;
foreach (Transform player in playGround)
{
if (player.name != ls.ToString()) continue;
Destroy(player.gameObject);
playersDict.Remove(ls);
list.Remove(ls);
}
}
}
catch (InvalidOperationException e) { Debug.LogError(e); }
'유니티(Unity)' 카테고리의 다른 글
Unity Android 빌드 시 라이트맵 어두움 (0) | 2024.03.16 |
---|---|
Collection was modified; enumeration operation may not execute. 에러. 리스트 요소 제거 시 주의 할점 (0) | 2024.02.17 |
Array,List,Dictionary 참조 변경 (0) | 2024.01.22 |
Unity D3D11 에러 해결방법 (0) | 2023.12.11 |
Unity glTF Importer (1) | 2023.11.01 |