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

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

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. 创建安全的基于HTTP的api应用接口

    #http://my.oschina.net/xiangtao/blog/196211 #要创建安全的基于HTTP的api接口,最重要的是要在服务端的进行请求的认证. #如何进行有效的服务端验证呢? ...

  2. 14、Java中用浮点型数据Float和Double进行精确计算时的精度问题

    一.浮点计算中发生精度丢失 大概很多有编程经验的朋友都对这个问题不陌生了:无论你使用的是什么编程语言,在使用浮点型数据进行精确计算时,你都有可能遇到计算结果出错的情况.来看下面的例子. // 这是一个 ...

  3. linux 2>&1的用法

    linux中有三种标准输入输出,分别是STDIN,STDOUT,STDERR,对应的数字是0,1,2.STDIN就是标准输入,默认从键盘读取信息:STDOUT是标准输出,默认将输出结果输出至终端,也就 ...

  4. OpenWrt 中安装配置Transmission

    参考文章https://wiki.openwrt.org/doc/uci/transmission 1. 安装包 必装的 transmission-daemon-openssl (后台服务)选装的 t ...

  5. Android C/C++ 开发

    标准的做法是 把 src 放到  /Android/develop/ 目录下去,然后建立 Android.mk 文件, 然后 make modue ...., 但是,这种方式 太麻烦. 下面介绍一种方 ...

  6. Spring(十二)使用Spring的xml文件配置方式实现AOP

    配置文件与注解方式的有非常大不同,多了非常多配置项. beans2.xml <?xml version="1.0" encoding="UTF-8"? & ...

  7. shell脚本死循环判断nginx日志reqest_time时间大于3秒是否增加,若增加发送相关日志信息到开发人员

    #!/bin/bash while [ 1 ] do pre_request_time_count=`cat /var/log/nginx/access.log |awk '{print $NF}'| ...

  8. SpringBoot优化内嵌的Tomcat ---设置MaxConnections

    使用kill -9杀掉springboot应用后,立马java -jar重启,会报错,需要等待一段时间才能启动成功,报错的原因是:/tmp/tomcat-docbase.474979491043437 ...

  9. 《JAVA与模式》之桥接模式

    桥接模式是一种结构型模式,它主要应对的是:由于实际的需要,某个类具有两个或两个以上的维度变化,如果只是用继承将无法实现这种需要,或者使得设计变得相当臃肿. 桥接模式的做法是把变化部分抽象出来,使变化部 ...

  10. pcurl 学习 http文件下载及写入空设备文件实例

    import pycurl import sys import os import random import string import time class Transaction(object) ...