BroadcastBlock缺少项目

时间:2017-06-04 04:29:11

标签: tpl-dataflow

我有一个需要处理的项目编号列表。一个项目可能有大约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() });
        }
    }

1 个答案:

答案 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