题目:模拟窗口卖票,四个窗口同时对外开放售票,需要按顺序售出。

要求:输出每一张票的售出时间和售出窗口,不能出现票未售出或者被售出多次的情况。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.Reflection;
using System.Diagnostics; namespace SellTicketsSynchronously
{
class Program
{
//入口
static void Main(string[] args)
{
Ticket tc = new Ticket();
Thread sellWindowA = new Thread(new ParameterizedThreadStart(SellTicket));
Thread sellWindowB = new Thread(new ParameterizedThreadStart(SellTicket));
Thread sellWindowC = new Thread(new ParameterizedThreadStart(SellTicket));
Thread sellWindowD = new Thread(new ParameterizedThreadStart(SellTicket));
sellWindowA.Name = "Window A";
sellWindowB.Name = "Window B";
sellWindowC.Name = "Window C";
sellWindowD.Name = "Window D";
sellWindowA.Start(tc);
sellWindowB.Start(tc);
sellWindowC.Start(tc);
sellWindowD.Start(tc);
sellWindowA.Join();
sellWindowB.Join();
sellWindowC.Join();
sellWindowD.Join();
Console.WriteLine("Tickets has been sold out. Press any key to quit:");
Console.ReadLine();
}
//卖票方法
public static void SellTicket(object obj)
{
Ticket ticket = obj as Ticket;
while (ticket.NumOfTickets>)
{
lock (ticket)
{
if (ticket.NumOfTickets > )
{
try
{
ticket.NumOfTickets--;
Console.WriteLine( DateTime.Now.ToString()+":"+Thread.CurrentThread.Name + " sells a ticket, " + ticket.NumOfTickets + " tickets left.");
}
catch (Exception ex)
{
WriteLog(ex);
}
}
}
Random random = new Random();
Thread.Sleep(random.Next(,));
}
}
//打log方法
private static void WriteLog(Exception ex)
{
string logUrl = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\SellTicketslog.txt";
if (File.Exists(@logUrl))
{
using (FileStream fs = new FileStream(logUrl, FileMode.Append))
{
using (StreamWriter sw = new StreamWriter(fs, Encoding.Default))
{
try
{
sw.Write(ex);
}
catch (Exception ex1)
{
WriteLog(ex1);
}
finally
{
sw.Close();
fs.Close();
}
}
}
}
else
{
using (FileStream fs = new FileStream(logUrl, FileMode.CreateNew))
{
using (StreamWriter sw = new StreamWriter(fs, Encoding.Default))
{
try
{
sw.Write(ex);
}
catch (Exception ex1)
{
WriteLog(ex1);
}
finally
{
sw.Close();
fs.Close();
}
}
}
}
}
}
//票类
class Ticket
{
public int NumOfTickets { get; set; }
public Ticket(int num)
{
this.NumOfTickets = num;
}
}
}

运行结果:

不知道这么写会不会有问题,求指点。

————————修改版——————————

经过园友指点,我改用了Task写了这段代码,其间得到了园友的帮助,非常感谢!

修改后的代码如下(蓝色字体为修改的部分):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.Reflection;
using System.Diagnostics; namespace SellTicketsSynchronously
{
class Program
{
//入口
static void Main(string[] args)
{
Ticket tc = new Ticket(10);
WaitForAllSales(tc);
Console.WriteLine("Tickets has been sold out. Press any key to quit:");
Console.ReadLine();
}
//售罄方法
private static void WaitForAllSales(Ticket tc)
{
//创建一个Task类型的泛型list
List<Task> tasks = new List<Task>();
for (int i = 1; i <= 4; i++)
{
//将所有的售票task存入list
tasks.Add(Task.Run(() => { SellTicket(string.Format("Window"+i), tc); }));
}
//等待所有的task都完成
Task.WaitAll(tasks.ToArray());
}
//卖票方法
public static void SellTicket(string windowName, object obj)
{
string nameOfWindow = windowName;
Ticket ticket = obj as Ticket;
while (ticket.NumOfTickets > )
{
lock (ticket)
{
if (ticket.NumOfTickets > )
{
try
{
ticket.NumOfTickets--;
Console.WriteLine(DateTime.Now.ToString() + ":" + nameOfWindow + " sells a ticket, " + ticket.NumOfTickets + " tickets left.");
}
catch (Exception ex)
{
WriteLog(ex);
}
}
}
Random random = new Random();
Thread.Sleep(random.Next(,));
}
}
//打log方法
private static void WriteLog(Exception ex)
{
string logUrl = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\SellTicketslog.txt";
if (File.Exists(@logUrl))
{
using (FileStream fs = new FileStream(logUrl, FileMode.Append))
{
using (StreamWriter sw = new StreamWriter(fs, Encoding.Default))
{
try
{
sw.Write(ex);
}
catch (Exception ex1)
{
WriteLog(ex1);
}
finally
{
sw.Close();
fs.Close();
}
}
}
}
else
{
using (FileStream fs = new FileStream(logUrl, FileMode.CreateNew))
{
using (StreamWriter sw = new StreamWriter(fs, Encoding.Default))
{
try
{
sw.Write(ex);
}
catch (Exception ex1)
{
WriteLog(ex1);
}
finally
{
sw.Close();
fs.Close();
}
}
}
}
}
}
//票类
class Ticket
{
public int NumOfTickets { get; set; }
public Ticket(int num)
{
this.NumOfTickets = num;
}
}
}

运行结果:

欢迎大家发散思维,继续提出宝贵意见!:)

------------------------------------------------------------------------------------------------------------

经过一位朋友细心的发现,上面这个程序逻辑是有问题的,一直都是售票窗口5在售票,修改后的代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.Reflection;
using System.Diagnostics; namespace SellTicketsSynchronously
{
class Program
{
//入口
static void Main(string[] args)
{
Ticket tc = new Ticket(20);
WaitForAllSales(tc);
Console.ReadLine();
}
//售罄方法
private static void WaitForAllSales(Ticket tc)
{
//创建一个Task类型的泛型list
List<Task> tasks = new List<Task>();
System.Random ran = new Random();
while (tc.NumOfTickets > 0)
{
int i = ran.Next(1,6);
//将所有的售票task存入list
tasks.Add(Task.Run(() => { SellTicket(string.Format("Window" + i), tc); }));
Task.WaitAll(tasks.ToArray());
}
Console.WriteLine("Tickets has been sold out. Press any key to quit:");
}
//卖票方法
public static void SellTicket(string windowName, object obj)
{
string nameOfWindow = windowName;
Ticket ticket = obj as Ticket;
lock (ticket)
{
if (ticket.NumOfTickets > )
{
try
{
ticket.NumOfTickets--;
Console.WriteLine(DateTime.Now.ToString() + ":" + nameOfWindow + " sells a ticket, " + ticket.NumOfTickets + " tickets left.");
}
catch (Exception ex)
{
WriteLog(ex);
}
}
Random random = new Random();
Thread.Sleep(random.Next(,));
}
}
//打log方法
private static void WriteLog(Exception ex)
{
string logUrl = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\SellTicketslog.txt";
if (File.Exists(@logUrl))
{
using (FileStream fs = new FileStream(logUrl, FileMode.Append))
{
using (StreamWriter sw = new StreamWriter(fs, Encoding.Default))
{
try
{
sw.Write(ex);
}
catch (Exception ex1)
{
WriteLog(ex1);
}
finally
{
sw.Close();
fs.Close();
}
}
}
}
else
{
using (FileStream fs = new FileStream(logUrl, FileMode.CreateNew))
{
using (StreamWriter sw = new StreamWriter(fs, Encoding.Default))
{
try
{
sw.Write(ex);
}
catch (Exception ex1)
{
WriteLog(ex1);
}
finally
{
sw.Close();
fs.Close();
}
}
}
}
}
}
//票类
class Ticket
{
public int NumOfTickets { get; set; }
public Ticket(int num)
{
this.NumOfTickets = num;
}
}
}

本次修改了售罄方法和入口方法(橙色字体),运行结果如下:

欢迎继续提出意见!谢谢大家~

C#中一道关于线程同步的练习题——模拟多窗口售票的更多相关文章

  1. C#中一道关于多线程的基础练习题——模拟仓库存销过程

    题目:模拟生产.入库.销售(50分) 假设某企业自产.自存.自销,需要将工厂生产的各类产品不定时的运到仓库,与此同时,需要将仓库中的货物运往超市和商场中进行销售,请编写一个程序模拟此过程(主要是存取这 ...

  2. 操作系统中的进程同步与Window中利用内核对象进行线程同步的关系

    操作系统中为了解决进程间同步问题提出了用信号量机制,信号量可分为四种类型分别是互斥型信号量,记录型信号量,AND型信号量,信号量集. 互斥型信号量 互斥型信号量是资源数量为1的特殊的记录型信号量.表示 ...

  3. Java多线程02(线程安全、线程同步、等待唤醒机制)

    Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...

  4. [C# 线程处理系列]专题四:线程同步

    目录: 一.线程同步概述 二.线程同步的使用 三 .总结 一.线程同步概述 前面的文章都是讲创建多线程来实现让我们能够更好的响应应用程序,然而当我们创建了多个线程时,就存在多个线程同时访问一个共享的资 ...

  5. C#线程学习笔记五:线程同步--事件构造

    本笔记摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/23/Event_Constructor.html,记录一下学习过程以备后续查用. 前面讲的线 ...

  6. java基础(27):线程安全、线程同步、等待唤醒机制

    1. 多线程 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的. 我们通过一个案例,演示线程 ...

  7. 第十七节:Runnable创建线程,Thread创建线程,唤醒线程和计数器多线程,线程同步与等待

    Runnable创建线程 public class RunnableDemo implements Runnable{ @Override public void run(){ int i = 1; ...

  8. java ->多线程_线程同步、死锁、等待唤醒机制

    线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的. l  我们通过一个案例,演示线 ...

  9. Linux 系统编程 学习:11-线程:线程同步

    Linux 系统编程 学习:11-线程:线程同步 背景 上一讲 我们介绍了线程的属性 有关设置.这一讲我们来看线程之间是如何同步的. 额外安装有关的man手册: sudo apt-get instal ...

随机推荐

  1. opencv-3.0.0-beta和opencv2版本号的差别

    我的机器:64位系统 第一步: opencv官网下载opencv3.0.0-beta版本号.解压到自己的目录,我的目录是E:\,解压后在E盘出现名为opencv的目录.该目录下有两个子目录 第二步:配 ...

  2. Oracle创建库

    oracle创建表空间 SYS用户在CMD下以DBA身份登陆: 在CMD中打sqlplus /nolog 然后再 conn / as sysdba --如果路径不存在则要创建路径 --创建临时表空间 ...

  3. Flask接通微信公众号

    import hashlib import xml.etree.ElementTree as ET from flask import Flask, request import time app = ...

  4. 彻底理解Python切片

    关于list的insert函数 list#insert(ind,value)在ind元素前面插入value 首先对ind进行预处理:如果ind<0,则ind+=len(a),这样一来ind就变成 ...

  5. 网络工程实训_4RIP路由(动态路由)

    实验4:RIP路由.包括RIPv1:RIPv2 动态路由协议包括距离向量路由协议和链路状态路由协议.RIP(Routing Information Protocol,路由信息协议)是使用最广泛的距离向 ...

  6. Android获取屏幕大小和设置无标题栏

    android获取屏幕大小非常常用,例如写个程序,如果要做成通用性很强的程序,适用屏幕很强,一般布局的时候都是根据屏幕的长宽来定义的,所以我把这个总结一下,方便日后忘记的时候查阅.还有就是有时候写程序 ...

  7. Linux命令-服务管理命令:chkconfig

    chkconfig --list 查看服务自启动状态列表,等同于查看服务列表 设置某一个服务为自启动服务: chkconfig 服务名 on 修改服务的启动级别为3,,5 查看某一个服务时候已经运行了 ...

  8. Linux命令-终止进程命令:kill

    kill -l 查看进程信号 常用信号 例如: 例子参见:Linux命令-查看进程命令:pstree

  9. 【Linux】文件种类与扩展名

    任何装置在Linux底下都是文件.不仅如此,连数据沟通的接口也有专属的文件在负责-所以,你会了解到,Linux的文件种类真的很多- Linux文件种类 我们在刚刚提到使用『ls -l』观察到第一栏那十 ...

  10. OAF_OAF控件系列9 - Description Flexfiled描述性弹性域的实现(案例)

    2014-06-17 Created By BaoXinjian