英文:
Asynchronous thread loses its name property
问题
In a process with many async threads, I set each thread to a specific name and monitor it regularly to see its status. Some threads seem to regularly lose their name value though. I can't find out why this happens.
public static void ChangeThreadName(this Thread current, string newName)
{
if (string.IsNullOrEmpty(current.Name))
{
try
{
current.Name = newName;
}
catch (InvalidOperationException ex)
{
Log.Error(ex, "Error when trying to change Threadname from [{current.Name}] to [{newName}]");
}
}
else
{
Log.Warning($"Trying to change Threadname from [{current.Name}] to [{newName}]");
}
}
Inside a while loop of an async task:
if (Thread.CurrentThread.ManagedThreadId != originalID)
{
Log.Verbose($"Thread change detected at ThreadHandler{QueueID}-{threadNumberString}, oldID = {originalID} - newID = {Thread.CurrentThread.ManagedThreadId}");
nr = await ThreadManager.ReRegisterProc(nr, threadName, false);
originalID = Thread.CurrentThread.ManagedThreadId;
}
if (Thread.CurrentThread.Name != threadName)
{
Log.Verbose("ThreadName changed to {status} -> reverting to {args}", Thread.CurrentThread.Name, threadName);
Thread.CurrentThread.ChangeThreadName(threadName);
}
Renaming and reregistering the task works, but I'd prefer it not being necessary.
I tried adding the threads to a static list
EDIT:
It is now clear that monitoring threads might not be the way to go. However, I still need to know which of all async threads are actually running and which are waiting. It regards a large variety of different threads which are created in classes that operate independently from each other.
I currently use this code:
public partial class ThreadManagement
{
private SemaphoreSlim KeySemaphore { get; } = new(1, 1);
public async Task<List<Thread>> GetThreads()
{
await Task.CompletedTask;
return TheseProcThreads.Values.ToList();
}
public async Task<Thread?> GetSpecificThread(long key)
{
if (TheseProcThreads.TryGetValue(key, out Thread FoundThread))
{
await Task.CompletedTask;
return FoundThread;
}
else
{
await Task.CompletedTask;
return null;
}
}
// Other methods in the ThreadManagement class...
}
public partial class ThreadManagement
{
private static SemaphoreSlim StaticKeySemaphore { get; } = new(1, 1);
private static ConcurrentDictionary<long, Thread> TheseProcThreads { get; } = new();
private static List<long> ReservedKeys { get; } = new();
}
英文:
In a process with many async threads, I set each thread to a specific name and monitor it regularly to see its status. Some threads seem to regularly lose their name value though. I can't find out why this happens.
public static void ChangeThreadName(this Thread current, string newName)
{
if (string.IsNullOrEmpty(current.Name))
{
try
{
current.Name = newName;
}
catch (InvalidOperationException ex)
{
Log.Error(ex, $"Error when trying to change Threadname from [{current.Name}] to [{newName}]");
}
}
else
{
Log.Warning($"Trying to change Threadname from [{current.Name}] to [{newName}]");
}
}
Inside a while loop of an async task:
if (Thread.CurrentThread.ManagedThreadId != originalID)
{
Log.Verbose($"Thread change detected at ThreadHandler{QueueID}-{threadNumberString}, oldID = {originalID} - newID = {Thread.CurrentThread.ManagedThreadId}");
nr = await ThreadManager.ReRegisterProc(nr, threadName, false);
originalID = Thread.CurrentThread.ManagedThreadId;
}
if (Thread.CurrentThread.Name != threadName)
{
Log.Verbose("ThreadName changed to {status} -> reverting to {args}", Thread.CurrentThread.Name, threadName);
Thread.CurrentThread.ChangeThreadName(threadName);
}
Renaming and reregistering the task works, but I'd prefer it not being necessary.
I tried adding the threads to a static list<thread> and looping it to see it's status, this doesn't work without reregistering as shown above.
EDIT:
It is now clear that monitoring threads might not be the way to go. However i still need to know which of all async threads are actually running and which are waiting. It regards a large variety of different threads which are created in classes that operate independent from eachother.
I currently use this code:
public partial class ThreadManagement
{
private SemaphoreSlim KeySemaphore { get; } = new(1, 1);
public async Task<List<Thread>> GetThreads()
{
await Task.CompletedTask;
return TheseProcThreads.Values.ToList();
}
public async Task<Thread?> GetSpecificThread(long key)
{
if (TheseProcThreads.TryGetValue(key, out Thread FoundThread))
{
await Task.CompletedTask;
return FoundThread;
}
else
{
await Task.CompletedTask;
return null;
}
}
public async Task<long> TryAddProc(long key, Thread value)
{
if (!TheseProcThreads.TryAdd(key, value))
{
key = await TryAddProc(await GetNewKey(), value);
}
return key;
}
public async Task<long> TryAddProc(long key, Thread value, string newName, ThreadPriority prio, bool background = false)
{
value.ChangeThreadName(newName);
value.Priority = prio;
value.IsBackground = background;
return await TryAddProc(key, value);
}
public async Task<long> TryAddProc(Thread value)
{
return await TryAddProc(await GetNewKey(), value);
}
public async Task<bool> TryRemoveProc(long key)
{
if (!TheseProcThreads.TryRemove(key, out _))
{
Log.Error("{object} {actie} {args} {status}.", "Thread", "verwijderen", "uit dictionary","mislukt");
await Task.CompletedTask;
return false;
}
await Task.CompletedTask;
return true;
}
public async Task<long> ReRegisterProc(long nr, string methodName, bool background)
{
Thread.CurrentThread.ChangeThreadName(methodName);
try
{
Thread.CurrentThread.IsBackground = background;
}
catch (Exception Ex)
{
Log.Error(Ex, "{actie} {object} {status}", "Instellen", "thread background", "mislukt");
}
await TryRemoveProc(nr);
return await TryAddProc(Thread.CurrentThread);
}
public async Task<long> GetNewKey()
{
await KeySemaphore.WaitAsync();
await StaticKeySemaphore.WaitAsync();
long tmpKey = TheseProcThreads.Count + 2;
while (true)
{
if (ReservedKeys.Contains(tmpKey))
{
tmpKey++;
}
else
{
break;
}
}
ReservedKeys.Add(tmpKey);
KeySemaphore.Release();
StaticKeySemaphore.Release();
return tmpKey;
}
}
public partial class ThreadManagement
{
private static SemaphoreSlim StaticKeySemaphore { get; } = new(1, 1);
private static ConcurrentDictionary<long, Thread> TheseProcThreads { get; } = new();
private static List<long> ReservedKeys { get; } = new();
}
答案1
得分: 4
在异步工作中,线程不是你的关注点。你不能,也不应该做任何依赖于特定线程的事情,包括(但不限于):
- 跨越
await
的Monitor
使用(或其他与线程绑定的锁定原语) [ThreadStatic]
- 线程名称 / id
- 线程本地存储
异步和线程是完全不同的概念。不存在所谓的“异步线程”。异步执行可能在不同的时间点(在任何await
可能切换的情况下)位于任意数量的不同线程上。
如果你真的需要一些环境状态,请考虑使用AsyncLocal<T>
,但说实话,避免使用环境状态是更可取的。
英文:
In async
work, the thread is none of your business. You cannot and should not do anything that depends on specific threads, including (but not limited to):
Monitor
usage (or other thread-bound locking primitives) that span anawait
[ThreadStatic]
- thread names / ids
- thread local storage
Async and threads are entirely different concepts. There is no such thing as an "async thread". There is an async execution that may find itself on one of any number of different threads at different points (potentially switching at any await
)
If you really need some ambient state, consider AsyncLocal<T>
, but honestly; avoiding ambient state is preferable
答案2
得分: 1
没有线程会失去其Name
属性。你可能会觉得这是发生了,但实际上并没有。
你所认为的“线程”并不是单一的线程,它实际上是在幕后不断创建和销毁的各种不同的线程。
这是因为async
使用了线程池(ThreadPool)。这意味着如果某个时刻有许多异步任务并行运行,将会创建新的线程,如果某个时刻没有太多或没有任务并行运行,旧线程可能会被销毁,而所有这些都完全不受你的控制(这也应该如此)。因此,新创建的线程将看起来没有名称,而死亡的线程将带着它们的名称一起消失。
我建议你给你的任务分配名称,而不是给线程分配名称。
英文:
No thread loses its Name
property. It may appear to you that this is happening, but it is not.
What you think of as a thread is not a single thread, it is various different threads that get created and destroyed all the time behind the scenes.
That's because async
makes use of threads from a ThreadPool.
This means that new threads will be created if at some moment you have many async tasks running in parallel, and old threads may be destroyed if at some moment you happen to have not so many, if any, tasks running in parallel, and all this is completely out of your control. (As it should, arguably.) Thus, newly created threads will appear to have no name, and threads that die will take their names to the grave with them.
I would recommend that you assign names to your tasks, not to the threads.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论