我有一个需要处理的项目编号列表。一个项目可能有大约8000个项目,我需要获取项目中每个项目的数据,然后将这些数据推送到服务器列表中。任何人都可以告诉我以下......
1)我在iR中有1000个项目,但只有998个被写入服务器。我是否通过使用broadCastBlock松散了项目? 2)我正在等待所有actionBlock正确吗? 3)如何使数据库调用异步?
这是数据库代码
public MemcachedDTO GetIR(MemcachedDTO dtoItem)
{
string[] Tables = new string[] { "iowa", "la" };
using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["test"].ConnectionString))
{
using (SqlCommand command = new SqlCommand("test", connection))
{
DataSet Result = new DataSet();
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@ProjectId", SqlDbType.VarChar);
command.Parameters["@ProjectId"].Value = dtoItem.ProjectId;
connection.Open();
Result.EnforceConstraints = false;
Result.Load(command.ExecuteReader(CommandBehavior.CloseConnection), LoadOption.OverwriteChanges, Tables);
dtoItem.test = Result;
}
}
return dtoItem;
}
更新 我已将代码更新到下面。它只是在我运行时挂起,只将1/4的数据写入服务器?你能告诉我我做错了吗?
public static ITargetBlock<T> CreateGuaranteedBroadcastBlock<T>(IEnumerable<ITargetBlock<T>> targets, DataflowBlockOptions options)
{
var targetsList = targets.ToList();
var block = new ActionBlock<T>(
async item =>
{
foreach (var target in targetsList)
{
await target.SendAsync(item);
}
}, new ExecutionDataflowBlockOptions
{
CancellationToken = options.CancellationToken
});
block.Completion.ContinueWith(task =>
{
foreach (var target in targetsList)
{
if (task.Exception != null)
target.Fault(task.Exception);
else
target.Complete();
}
});
return block;
}
[HttpGet]
public async Task< HttpResponseMessage> ReloadItem(string projectQuery)
{
try
{
var linkCompletion = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 2
};
var cts = new CancellationTokenSource();
var dbOptions = new DataflowBlockOptions { CancellationToken = cts.Token };
IList<string> projectIds = projectQuery.Split(',').ToList();
IEnumerable<string> serverList = ConfigurationManager.AppSettings["ServerList"].Split(',').Cast<string>();
var iR = new TransformBlock<MemcachedDTO, MemcachedDTO>(
dto => dto.GetIR(dto), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 3 });
List<ActionBlock<MemcachedDTO>> actionList = new List<ActionBlock<MemcachedDTO>>();
List<MemcachedDTO> dtoList = new List<MemcachedDTO>();
foreach (string pid in projectIds)
{
IList<MemcachedDTO> dtoTemp = new List<MemcachedDTO>();
dtoTemp = MemcachedDTO.GetItemIdsByProject(pid);
dtoList.AddRange(dtoTemp);
}
foreach (string s in serverList)
{
var action = new ActionBlock<MemcachedDTO>(
async dto => await PostEachServerAsync(dto, s, "setitemcache"));
actionList.Add(action);
}
var bBlock = CreateGuaranteedBroadcastBlock(actionList, dbOptions);
foreach (MemcachedDTO d in dtoList)
{
await iR.SendAsync(d);
}
iR.Complete();
iR.LinkTo(bBlock);
await Task.WhenAll(actionList.Select(action => action.Completion).ToList());
return Request.CreateResponse(HttpStatusCode.OK, new { message = projectIds.ToString() + " reload success" });
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, new { message = ex.Message.ToString() });
}
}
答案 0 :(得分:0)
1)我在iR中有1000个项目,但只有998个被写入服务器。我是否通过使用broadCastBlock松散了项目?
是的,在下面的代码中,您将BoundedCapacity
设置为1,如果您的BroadcastBlock
无法在任何时间传递项目,则会将其删除。此外,BroadcastBlock
只会将Completion
传播到一个TargetBlock
,请勿在此处使用PropagateCompletion=true
。如果要完成所有块,则需要手动处理Completion
。这可以通过将ContinueWith
上的BroadcastBlock
设置为将Completion
传递给所有已连接的目标来完成。
var action = new ActionBlock<MemcachedDTO>(dto => PostEachServerAsync(dto, s, "set"), new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 3, BoundedCapacity = 1 });
broadcast.LinkTo(action, linkCompletion);
actionList.Add(action);
选项:而不是BroadcastBlock
使用正确有界BufferBlock
。当您的下游块绑定到一个项目时,他们无法接收其他项目,直到他们完成处理所拥有的项目。这将允许BufferBlock
将其项目提供给另一个,可能是空闲的,ActionBlock
。
将项目添加到限制流程中时,即BoundedCapacity
小于Unbounded
的流程。您需要使用SendAsync
方法或至少处理Post
的返回。我建议您只使用SendAsync
:
foreach (MemcachedDTO d in dtoList)
{
await iR.SendAsync(d);
}
这会强制您的方法签名变为:
public async Task<HttpResponseMessage> ReloadItem(string projectQuery)
2)我正在对所有actionBlocks进行正确的等待吗?
之前的更改将允许您放弃阻止Wait
调用以支持await Task.WhenAlll
iR.Complete();
actionList.ForEach(x => x.Completion.Wait());
To:
iR.Complete();
await bufferBlock.Completion.ContinueWith(tsk => actionList.ForEach(x => x.Complete());
await Task.WhenAll(actionList.Select(action => action.Completion).ToList());
3)如何使数据库调用异步?
我要保持开放,因为它应该是与TPL-Dataflow
无关的单独问题,但简而言之,使用async
Api访问您的Db和async
将通过你的代码库自然增长。 This should get you started
BufferBlock与BroadcastBlock
重新阅读previous question和@VMAtm的答案。您似乎希望将每个项目发送到 所有 五台服务器,在这种情况下,您需要BroadcastBlock
。您可以使用BufferBlock
将消息相对均匀地分发到灵活的服务器池,每个服务器都可以处理消息。尽管如此,您仍然需要通过等待ActionBlocks
的完成来控制传播完成和故障到所有连接的BroadcastBlock
。
防止BroadcastBlock丢弃的消息
一般情况下,您有两个选项,将ActionBlocks
设置为未绑定,这是默认值:
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 3, BoundedCapacity = Unbounded });
或从您自己的各种建筑中广播自己的信息。来自@ i3arnon的Here is an example implementation。来自@svick的And another