.NET 并行(多核)编程系列之七 共享数据问题和解决概述

摘要:
如果你熟悉多线程编程,你就知道上述情况是由“共享数据竞争”造成的。当我们与多个任务共享数据时,可能会出现问题。注意:同步与以前的多线程或数据库操作中的同步不同。3.顺序执行的解决方案通过一次仅一个任务访问共享数据来解决数据竞争问题。本质上,此解决方案返回到以前的单线程编程模型。

 

  前言:之前的文章介绍了了并行编程的一些基础的知识,从本篇开始,将会讲述并行编程中实际遇到一些问题,接下来的几篇将会讲述数据共享问题。

 本篇的议题如下:      

  1. 数据竞争
  2. 解决方案提出
  3. 顺序的执行解决方案
  4. 数据不变解决方案

在开始之前,首先,我们来看一个很有趣的例子:

 

 10task,每个task都是把BankAccount.Balance自增1000次。之后代码就等到10task执行完毕,然后打印出Balance的值。大家猜想一下,上次的代码执行完成之后,打印出来的Balance的结果是多少?

.NET 并行(多核)编程系列之七 共享数据问题和解决概述第1张.NET 并行(多核)编程系列之七 共享数据问题和解决概述第2张代码
class BankAccount
{
    
public int Balance
    {
        
get;
        
set;
    }
}
class App
{
    
static void Main(string[] args)
    {
        
// create the bank account instance
        BankAccount account = new BankAccount();
        
// create an array of tasks
        Task[] tasks = new Task[10];
        
for (int i = 0; i < 10; i++)
        {
            
// create a new task
            tasks[i] = new Task(() =>
            {
                
// enter a loop for 1000 balance updates
                for (int j = 0; j < 1000; j++)
                {
                    
// update the balance
                    account.Balance = account.Balance + 1;
                }
            });
            
// start the new task
            tasks[i].Start();
        }

        
// wait for all of the tasks to complete
        Task.WaitAll(tasks);

        
// write out the counter value
        Console.WriteLine("Expected value {0}, Counter value: {1}",
        
10000, account.Balance);

        
// wait for input before exiting
        Console.WriteLine("Press enter to finish");
        Console.ReadLine();
    }
}

在上面的例子中,创建了

  

J结果确实和大家猜想的一样:结果不等于10000。每次执行一次上面的代码,都会得到不同的结果,而且这些结果值都在10000左右,如果运气好,可能看到有那么一两次结果为10000.为什么会这样?

下面就是本篇和接下来的几篇文章要讲述的内容。

 

  1. 数据竞争

如果大家对多线程编程比较熟悉,就知道上面情况的产生是因为共享数据竞争导致的(对多线程不熟悉不清楚的朋友也不用担心)。当有两个或者更多的task在运行并且操作同一个共享公共数据的时候,就存在潜在的竞争。如果不合理的处理竞争问题,就会出现上面意想不到的情况。

下面就来分析一下:上面代码的情况是怎么产生的。

当在把account对象的Balance进行自增的时候,一般执行下面的三个步骤:

  1. 读取现在account对象的Balance属性的值。
  2. 计算,创建一个临时的新变量,并且把Balance属性的值赋值给新的变量,而且把新变量的值增加1
  3. 把新变量的值再次赋给accountBalance属性

在理论上面,上面的三个步骤是代码的执行步骤,但是实际中,由于编译器,.NET 运行时对自增操作的优化操作,和操作系统等的因素,在执行上面代码的时候,并不一定是按照我们设想的那样运行的,但是为了分析的方便,我们还是假设代码是按照上面的三个步骤运行的。

之前的代码每次执行一次,执行代码的计算机就每次处于不同的状态:CPU的忙碌状况不同,内存的剩余多少不同,等等,所以每次代码的运行,计算机不可能处于完全一样的环境中。

 

在下面的图中,显示了两个task之间是如何发生竞争的。当两个task启动了之后(虽然说是并行运算,但是不管这样,两个的task的执行时间不可能完全一样,也就是说,不可能恰好就是同时开始执行的,起码在开始执行的时间上是有一点点的差异的)。

 

  

1.    首先Task1读取到当前的balance的值为0

2.    然后,task2运行了,并且也读取到当前的balance值为0

3.    两个task都把balance的值加1

4.    Task1balance的值加1后,把新的值保存到了balance

5.    Task2 也把新的保存到了balance

所以,结果就是:虽然两个task 都为balance1,但是balance的值还是1

通过这个例子,相信大家应该清楚,为什么上面的10task执行1000,而执行后的结果不是10000了。 

 

 

2.  解决方案提出

 

数据竞争就好比一个生日party。其中,每一个task都是参加party的人,当生日蛋糕出来之后,每个人都兴奋了。如果此时,所有的人都一起冲过去拿属于他们自己的那块蛋糕,此时party就一团糟了,没有如何顺序。

在之前的图示例讲解中,balance那个属性就好比蛋糕,因为task1task2都要得到它,然后进行运算。当我们来让多个task共享一个数据时就可能出现问题。下面列出了四种解决方案:

1.    顺序执行:也就是让第一个task执行完成之后,再执行第二个。

2.    数据不变:我们让task不能修改数据。

3.    隔离:我们不共享数据,让每个task都有一份自己的数据拷贝。

4.    同步:通过调整task的执行,有序的执行task

注意:同步和以前多线程中的同步,或者数据库操作时的同步概念不一样

 

 

3. 顺序的执行的解决方案

顺序的执行解决了通过每次只有一个task访问共享数据的方式解决了数据竞争的问题,其实在本质上,这种解决方案又回到了之前的单线程编程模型。如果拿之前的party分蛋糕的例子,那么现在就是一次只能允许一个人去拿蛋糕。

 

  1. 数据不变解决方案

 

数据不变的解决方案就是通过让数据不能被修改的方式来解决共享数据竞争。如果拿之前的蛋糕为例子,那么此时的情况就是:现在蛋糕只能看,不能吃。

C#中,可以同关键字 readonly const来声明一个字段不能被修改:

public const int AccountNumber=123456;

被声明为const的字段只能通过类型来访问:如,上面的AccountNumber是在Blank类中声明的,那么访问的方式就是Blank. AccountNumber

readonly的字段可以在实例的构造函数中修改。

如下代码:

  

.NET 并行(多核)编程系列之七 共享数据问题和解决概述第3张.NET 并行(多核)编程系列之七 共享数据问题和解决概述第4张代码
using System;

class ImmutableBankAccount
{
    
public const int AccountNumber = 123456;
    
public readonly int Balance;
    
public ImmutableBankAccount(int InitialBalance)
    {
        Balance 
= InitialBalance;
    }
    
public ImmutableBankAccount()
    {
        Balance 
= 0;
    }
}

class App
{
    
static void Main(string[] args)
    {
        
// create a bank account with the default balance
        ImmutableBankAccount bankAccount1 = new ImmutableBankAccount();
        Console.WriteLine(
"Account Number: {0}, Account Balance: {1}",

        ImmutableBankAccount.AccountNumber, bankAccount1.Balance);

        
// create a bank account with a starting balance
        ImmutableBankAccount bankAccount2 = new ImmutableBankAccount(200);
        Console.WriteLine(
"Account Number: {0}, Account Balance: {1}",
        ImmutableBankAccount.AccountNumber, bankAccount2.Balance);

        
// wait for input before exiting
        Console.WriteLine("Press enter to finish");
        Console.ReadLine();
    }
}

 

数据不变的解决方案不是很常用,因为它对数据限制太大了。

 

今天暂时就写到这里,谢谢大家。

 

   版权为小洋和博客园所有,转载请标明出处给作者。

   http://www.cnblogs.com/yanyangtian

 

 

.NET 并行(多核)编程系列之七 共享数据问题和解决概述第5张

免责声明:文章转载自《.NET 并行(多核)编程系列之七 共享数据问题和解决概述》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇2D情况下,复数的意义代表旋转ASMFD的使用过程中遇到的问题下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

sql 排序时某些数据指定在前面然后再order by

      有时候order by有这种需求:需要某个userid的数据排在第一行,然后再跟进其他字段进行order by 。 怎么处理这种需求。可以这么用: select * from user order by  case when userid =xx then  0 else userid end --我们把userid等于指定数值的设置为0这样就...

mysql8.0 创建数据库及对应的用户

1、使用root用户登入数据库 2、创建数据库 create dababase test_database 3、创建用户 ## % 代表不限制ip ## localhost 代表只能本地访问 ## 192.168.1.1 代表只能192.168.1.1 这个固定ip访问 create user 'testuser'@'%' identified by '...

基于SAP的中国式数据分析浅谈

大数据时代,虽然多数企业数据的应用并不能称得上是“大数据”,但也证实了数据应用的重要性和影响力。确实,数据作为企业发展的信息沉淀,已成为企业的重要资产,如何有效利用数据是每个企业必须面临的课题。 这里我们分享一个波司登集团通过改进信息系统,加强数据运营管理来提升企业效益的案例,原文是波司登集团软件研发经理孙健在帆软大数据巡展上的演讲。 从2012年到201...

结构化数据、半结构化数据、非结构化数据——Hadoop处理非结构化数据

刚开始接触Hadoop ,指南中说Hadoop处理非结构化数据,学习数据库的时候,老师总提结构化数据,就是一张二维表,那非结构化数据是什么呢?难道是文本那样的文件?经过上网搜索,感觉这个帖子不错 网址:http://blog.sina.com.cn/s/blog_49c1385f01014bf6.html 1. 结构化数据(structured data)...

R语言-文本挖掘

---恢复内容开始--- 案例1:对主席的新年致辞进行分词,绘制出词云 掌握jieba分词的用法 1.加载包 library(devtools) library(tm) library(jiebaR) library(jiebaRD) library(tmcn) library(NLP)library(wordcloud2) 2.导入数据 new...

C#如何为程序打包发布应用(图解教程) (转)

1:新建安装部署项目 打开VS,点击新建项目,选择:其他项目类型->安装与部署->安装向导(安装项目也一样),然后点击确定.(详细见下图) 此主题相关图片如下: 2:安装向导关闭后打开安装向导,点击下一步,或者直接点击完成. 3:开始制作 安装向导完成后即可进入项目文件夹: 双击"应用程序文件夹"在右边的空白处右击,选择添加->文件,将你...