我有一个web api方法启动可能需要很长时间的工作,所以我想将这项工作交给新线程并返回给调用者。
在下面的代码中,我定义了一个将启动n个工作流程的Action。我的问题涉及在Action方法之外定义的变量。
在Action中使用memberManager和workflowDefinitionName是否安全?
当InitiateBulkDischargeWorkflow方法立即返回时,memberManager和workflowDefinitionName会继续在Action代码块中可用吗?
我之前没有做过很多线程,因此我正在努力提高自己的知识,并且到目前为止我正在寻找有关代码的反馈。
示例代码:
public class MemberController : ApiController {
private readonly IMemberManager memberManager;
public MemberContoller(IMemberManager memberManager) {
this.memberManager = memberManager;
}
protected int CurrentUserId {
get {
// removed
}
}
[System.Web.Http.HttpPost]
public void InitiateBulkDischargeWorkflow(string workflowDefinitionName, IDictionary<string, object> args)
{
args["argInitiatorId"] = CurrentUserId;
// removed - ensure args are valid
Action<object> action = (x) =>
{
var args2 = (IDictionary<string, object>) x;
foreach (var memberId in (List<int>)args["argMemberIds"]) {
try {
// create new dictionary
var memberArgs = new Dictionary<string, object>();
memberArgs["argMemberId"] = memberId;
memberArgs["argInitiatorId"] = args2["argInitiatorId"];
// further dictionary configuration here
// long running process
memberManager.InitiateWorkflow(initiatorId, memberId, workflowDefinitionName, memberArgs);
} catch (Exception e) {
log.ErrorFormat("Error initiating workflow '{0}' for memberid={1} by userid={2} in InitiateBulkDischargeWorkflow", e,
workflowDefinitionName, memberId, CurrentUserId);
}
}
};
// start thread
System.Threading.Tasks.Task.Factory.StartNew(action, args);
// return immediately, let thread handle creation of workflows
return;
}
答案 0 :(得分:0)
如果代码编译,则变量可供使用。是否安全是另一回事。在你的问题中,鉴于到目前为止显示的代码,最好的人可以说是“它取决于”。
workflowDefinitionName
变量可以肯定。变量本身是一个局部变量,不是由命名方法或内部的匿名方法修改的,而且对象是string
,它是不可变的,本质上是线程安全的。
memberManager
变量不同。变量本身,可能是MemberController
类型的变量本身,尽管你的代码示例没有说明这一点,但是在匿名方法中可以像你一样使用它。但请注意,如果您有多个线程一次访问对象,则可能需要添加同步以确保安全发生。
如果没有更好的代码示例,我不能说需要什么样的同步。但是你可能需要为这个对象提供一些东西。
答案 1 :(得分:0)
您的代码似乎过于复杂。越复杂,它就越有可能出错。
当您使用值args2["argInitiatorId"]
构建memberArgs
字典时,您正在做一些奇怪的间接。 args2["argInitiatorId"]
的值只是CurrentUserId
,为什么不直接使用它?
此外,永远不要catch (Exception e)
。它是引入错误并使代码可以解除代码的最佳方法之一。您应该只捕获您尝试处理的特定异常。
我的建议是让您的代码更简单:
public void InitiateBulkDischargeWorkflow(
string workflowDefinitionName,
IDictionary<string, object> args)
{
var tasks =
from memberId in args["argMemberIds"] as List<int>
let memberArgs = new Dictionary<string, object>()
{
{ "argMemberId", memberId },
{ "argInitiatorId", CurrentUserId },
}
select Task.Run(() =>
memberManager.InitiateWorkflow(
initiatorId,
memberId,
workflowDefinitionName,
memberArgs));
Task.WhenAll(tasks);
}
我希望这会有所帮助。