【发布时间】:2022-02-18 16:09:38
【问题描述】:
我只是使用简单的锁来管理一些共享数据。
有没有办法知道哪个线程获得了锁?
基本上有人已经获得了锁并且没有释放它。因此,所有后续操作都只是在锁定和超时时触发。
我有点卡住了,因为从调试器开始它只是卡在锁定状态,因为有人已经获取了它,我在“Break All”之后查看了“Debugger + Windows+ Threads” - 没有线程进入锁定。
它没有显示任何进入临界区的线程。
有可能有人获得了锁并且该线程已被中止。但我希望即使线程已中止,锁也会被释放。我的预期错了吗?
这是我的课程 - 基本上它是一个 powershell cmd 运行程序,并且可以从多个线程执行 cmd:
internal abstract class PowerShellCommandRunner : IDisposable
{
#region Fields
protected object m_syncObject = new object();
private PSSession m_psSession = null;
private Runspace m_runspace = null;
#endregion
#region Constructor
public PowerShellCommandRunner(ExchangeApplicationSystem system)
{
if (null == system)
{
throw new ArgumentNullException("ExchangeApplicationSystem");
}
this.ExchangeApplicationSystem = system;
this.PSCredential = this.ExchangeApplicationSystem.Credential.GetPSCredential();
}
#endregion
#region Properties
internal ExchangeApplicationSystem ExchangeApplicationSystem
{
get;
private set;
}
public PSCredential PSCredential
{
get;
protected set;
}
private bool IsNotInitializedOrInvalidRunspace(Runspace runspace)
{
bool flag = (null == runspace) //not initialized
|| (null == runspace.RunspaceStateInfo) //not state info (defensive)
|| (runspace.RunspaceStateInfo.State == RunspaceState.Broken) //runspace state is broken
|| (null != runspace.RunspaceStateInfo.Reason); //there is an exception
return flag;
}
private bool NeedToCreatePsSession
{
get
{
bool flag = (null == this.m_psSession)
|| this.IsNotInitializedOrInvalidRunspace(this.m_psSession.Runspace);
return flag;
}
}
internal Runspace Runspace
{
get
{
lock (this.m_syncObject)
{
if (this.IsNotInitializedOrInvalidRunspace(this.m_runspace))
{
if (null != this.m_runspace)
{
//already have one runspace - close it, before we create another one
this.CloseRunspace();
}
this.m_runspace = RunspaceFactory.CreateRunspace();
this.m_runspace.Open();
}
return this.m_runspace;
}
}
}
#endregion
#region Methods
internal IEnumerable<PSObject> Execute(string cmd, params object[] argumentList)
{
if (string.IsNullOrEmpty(cmd))
{
throw new ArgumentNullException("cmd");
}
return this.Execute(new Command(cmd), argumentList);
}
/// <summary>
/// Sub-classes can do their own specific implementation to create ps-sessions
/// The base class simply performs primitive oepratiosn like managing them.
/// </summary>
/// <returns></returns>
internal abstract PSSession GetPSSession();
/// <summary>
/// Gets the pssession and if reuired updates it as well
/// </summary>
/// <returns></returns>
private PSSession GetAndUpdateManagePSSessionInfRequired()
{
//Note: we dont need to lock as the callers (Exceute methods) will be acquiring the lock before executing the code
//but, just locking it again as the locks are re-entrant
lock (this.m_syncObject)
{
if (this.NeedToCreatePsSession)
{
if (null != this.m_psSession
&& (null != this.m_runspace))
{
//if ps-session exists, remove it from runspace
this.RemovePsSessionFromRunspace();
//Yes, there can be a case where some one already have a reference to the remove session
//that's ok, as the operation simply throws
//And subsequently they will be releasing and re-using the new one.
this.m_psSession = null;
}
//now, open a new session (requesting for a new session from subclasses)
this.m_psSession = this.GetPSSession();
Debug.Assert(null != this.m_psSession);
}
return this.m_psSession;
}
}
internal IEnumerable<PSObject> Execute(Command cmd, params object[] argumentList)
{
if (null == cmd)
{
throw new ArgumentNullException("cmd");
}
lock (this.m_syncObject)
{
//Pipelines cannot be executed concurrently, so serialize it
OperationProgressReporter.Report(string.Format("Executing the following PowerShell Command: {0}", cmd.ToString()));
return this.Runspace.ExecuteCommand(cmd, this.GetAndUpdateManagePSSessionInfRequired(), argumentList);
}
}
internal IEnumerable<PSObject> Execute(ScriptBlock sb, params object[] argumentList)
{
if (null == sb)
{
throw new ArgumentNullException("scriptblock");
}
lock (this.m_syncObject)
{
//Pipelines cannot be executed concurrently, so serialize it
OperationProgressReporter.Report(string.Format("Executing the following PowerShell Command: {0}", sb.ToString()));
return this.Runspace.ExecuteCommand(sb, this.GetAndUpdateManagePSSessionInfRequired(), argumentList);
}
}
private void RemovePsSessionFromRunspace()
{
//not intended to call before acquiring a lock
//(For ex: either while closing runspace or while getting rid of old session and getting a new one (GetAndUpdateManagePSSessionInfRequired)
//but locking it as the locks are re-entrant (defensive)
lock (this.m_syncObject)
{
if ((null != this.m_psSession)
&& (null != this.m_runspace))
{
try
{
string errorMsg = null;
this.m_runspace.RemovePsSessionFromRunspace(this.m_psSession, out errorMsg);
if (!string.IsNullOrEmpty(errorMsg))
{
FxTracing.TraceError(TraceEventId.GeneralError, errorMsg);
}
}
catch (Exception ex)
{
ExceptionManager.GeneralExceptionFilter(ex);
OperationProgressReporter.Report(OperationProgressMessageLevel.Verbose,
string.Format("<DEBUG> Unable to remove PSsession from runspace in '{0}'", this.ExchangeApplicationSystem));
}
}
}
}
private void CloseRunspace()
{
//lcok it
lock (this.m_syncObject)
{
//check again to make sure only one thread enters
if (null != this.m_runspace)
{
try
{
//if a ps-session is created, remove it
if (null != this.m_psSession)
{
//remove the pssession from runspace
this.RemovePsSessionFromRunspace();
}
//then close the runspace
this.m_runspace.Close();
}
catch (Exception ex)
{
//swallow
ExceptionManager.GeneralExceptionFilter(ex);
Debug.Fail(ex.Message);
}
finally
{
//finally, set the runspace to null
//Yes, the runspace can be set to null while another thread can have a reference to old runspace
//its ok as the operation simply fail with invalid runspace state exception (most likely)
//And when they retry they get the updated runspace or get a new one.
//same appraoch as managing ps-session
this.m_runspace = null;
}
}
}
}
#endregion
#region IDisposable
public void Dispose()
{
this.CloseRunspace();
}
#endregion
}
【问题讨论】:
-
可以分享代码吗?
-
请提供有关您的线程模型和锁的更多信息。首选代码。
-
当然,添加代码。
-
您期望即使持有它的线程被中止也会释放锁是合理的。任何未执行的
finally块都会在线程中止之前执行(参见msdn.microsoft.com/en-us/library/ty8d3wta.aspx)。请注意,如果调用 .Abort() 的线程持有其他线程的finally区域所需的任何资源的锁,这可能会导致死锁。 -
您显然已经对这段代码进行了一些思考。不过有一件事引起了我的注意(这纯粹是猜测):您正在从许多受锁保护的代码区域调用
OperationProgressReporter.Report。这可能会执行一些外部代码。如果该代码碰巧将同步消息分派到另一个同步上下文,而后者又调用同一个PowerShellCommandRunner实例上的方法或属性访问器之一(命中同一个锁),那么您就陷入了死锁。
标签: c# .net visual-studio